package com.zmj.sy.mom.srv.aps.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.zmj.sy.mom.srv.aps.bean.bo.*;
import com.zmj.sy.mom.srv.aps.bean.bo.plan.PlanFactoryCache;
import com.zmj.sy.mom.srv.aps.bean.bo.plan.PlanOrderCache;
import com.zmj.sy.mom.srv.aps.bean.entity.base.BaseEntity;
import com.zmj.sy.mom.srv.aps.bean.entity.order.*;
import com.zmj.sy.mom.srv.aps.bean.entity.resource.*;
import com.zmj.sy.mom.srv.aps.bean.vo.base.BaseListResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.nestpre.LantekPrePushReqVo;
import com.zmj.sy.mom.srv.aps.bean.vo.order.OrderScheduleReqVo;
import com.zmj.sy.mom.srv.aps.bean.vo.plan.*;
import com.zmj.sy.mom.srv.aps.config.ProjectConfig;
import com.zmj.sy.mom.srv.aps.mapper.*;
import com.zmj.sy.mom.srv.aps.utils.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@RequiredArgsConstructor
@Service
public class PlanService {


    private final ReverseOrderMapper reverseOrderMapper;
    private final OrderMapper orderMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final ApsOrderMapper apsOrderMapper;
    private final ReverseDetailMapper reverseDetailMapper;
    private final ApsBatchMapper apsBatchMapper;
    private final ApsWorkOrderMapper apsWorkOrderMapper;
    private final ApsWorkDetailMapper apsWorkDetailMapper;
    private final ApsDetailWeekdayMapper apsDetailWeekdayMapper;

    private final ProjectConfig projectConig;
    private final CustomerMapper customerMapper;
    private final OrderPhaseMapper orderPhaseMapper;
    private final GroupMapper groupMapper;
    private final StationMapper stationMapper;
    private final StationWeekdaySchemeMapper stationWeekdaySchemeMapper;
    private final StationWeekdayRuleMapper stationWeekdayRuleMapper;
    private final ShiftTypeMapper shiftTypeMapper;
    private final ShiftMapper shiftMapper;
    private final BreakPeriodMapper breakPeriodMapper;
    private final OrderBomMapper orderBomMapper;
    private final PhaseExtMapper phaseExtMapper;
    private final PhaseJobMapper phaseJobMapper;
    private final PhaseJobRefMapper phaseJobRefMapper;
    private final PhaseMapper phaseMapper;
    private final WeekdayRuleMapper weekdayRuleMapper;
    private final NestService nestService;

    @Transactional
    public void scheduling(PlanSchedulingReqVo reqVo) {
        log.info("scheduling reqVo: {}", JsonUtils.toJsonString(reqVo));

        PlanOrderCache orderCache = new PlanOrderCache();
        PlanFactoryCache factoryCache = new PlanFactoryCache();

        orderCache.setSchedulingSource("erp");

        initOrderAndApsOrder(reqVo, orderCache);

        valid(orderCache);

        lock(orderCache);

        clear(orderCache);

        initFactoryCache(factoryCache, orderCache);

        initOrderCache(factoryCache, orderCache);

        scheduleTask(factoryCache, orderCache);

        saveAndUpate(orderCache);

    }

    public void saveAndUpate(PlanOrderCache orderCache) {

        // 保存所有的workOrder
        List<ApsWorkOrderTemp> collect = orderCache.getApsWorkOrderTempList().stream().filter(e -> e.getPid() == null).collect(Collectors.toList());
        Map<Integer, ApsWorkOrderTemp> apsWorkOrderTempIdMap = orderCache.getApsWorkOrderTempIdMap();
        Map<Integer, List<ApsWorkOrderTemp>> apsWorkOrderTempPidMap = orderCache.getApsWorkOrderTempPidMap();

        List<ApsWorkOrderTemp> children = new ArrayList<>(collect);
        while (CollUtil.isNotEmpty(children)) {
            List<ApsWorkOrder> saveBatch = children.stream()
                    .map(e -> {
                        if (e.getPid() != null) {
                            e.getApsWorkOrder().setPid(apsWorkOrderTempIdMap.get(e.getPid()).getApsWorkOrder().getId());
                        }
                        return e.getApsWorkOrder();
                    })
                    .collect(Collectors.toList());
            apsWorkOrderMapper.insertBatch(saveBatch, 1000);

            List<ApsWorkOrderTemp> temp = new ArrayList<>();

            for (ApsWorkOrderTemp child : children) {
                List<ApsWorkOrderTemp> apsWorkOrderTemps = apsWorkOrderTempPidMap.get(child.getId());
                if (CollUtil.isNotEmpty(apsWorkOrderTemps)) {
                    temp.addAll(apsWorkOrderTemps);
                }
            }

            children = temp;
        }

        // 保存所有的workDetail
        for (ApsTask task : orderCache.getTaskList()) {
            ApsWorkOrderTemp apsWorkOrderTemp = apsWorkOrderTempIdMap.get(task.getApsWorkOrderId());
            task.getApsWorkDetailTemp().getApsWorkDetail().setApsWorkOrderId(apsWorkOrderTemp.getApsWorkOrder().getId());
            task.getApsWorkDetailTemp().getApsWorkDetail().setApsWorkOrderPid(apsWorkOrderTemp.getApsWorkOrder().getPid());
        }
        List<ApsWorkDetail> apsWorkDetailList = orderCache.getTaskList().stream().map(ApsTask::getApsWorkDetail).collect(Collectors.toList());
        apsWorkDetailMapper.insertBatch(apsWorkDetailList, 2000);


        // 保存所有的apsDetailWeekday
        for (ApsTask task : orderCache.getTaskList()) {
            for (ApsWeekDayWrap apsWeekDayWrap : task.getAll()) {
                apsWeekDayWrap.setApsWorkOrderId(task.getApsWorkDetail().getApsWorkOrderId());
                apsWeekDayWrap.setApsWorkDetailId(task.getApsWorkDetail().getId());
            }
        }
        List<ApsDetailWeekday> apsDetailWeekdayList = orderCache.getTaskList()
                .stream()
                .flatMap(e -> e.getAll().stream().map(machineCalendar -> {
                    ApsDetailWeekday calendar = new ApsDetailWeekday();

                    calendar.setOrderId(e.getApsWorkDetail().getOrderId());
                    calendar.setApsOrderId(e.getApsWorkDetail().getApsOrderId());
                    calendar.setApsBomId(e.getApsWorkDetail().getApsWorkOrderId());
                    calendar.setPhaseId(e.getApsWorkDetail().getPhaseId());
                    calendar.setPhaseCode(e.getApsWorkDetail().getPhaseCode());
                    calendar.setPhaseName(e.getApsWorkDetail().getPhaseName());
                    calendar.setApsWorkOrderId(e.getApsWorkDetail().getApsWorkOrderId());
                    calendar.setApsWorkDetailId(e.getApsWorkDetail().getId());
                    calendar.setGroupId(e.getApsWorkDetail().getGroupId());

                    calendar.setWorkTime(machineCalendar.getSecond());
                    calendar.setPlanStartDateTime(machineCalendar.getStart());
                    calendar.setPlanEndDateTime(machineCalendar.getEnd());
                    calendar.setShiftId(machineCalendar.getShiftId());
                    calendar.setShiftSeq(machineCalendar.getShiftSeq());
                    calendar.setShiftStartDateTime(machineCalendar.getShiftStartDateTime());
                    calendar.setShiftEndDateTime(machineCalendar.getShiftEndDateTime());
                    calendar.setShiftDay(machineCalendar.getShiftDay());
                    calendar.setStationGroupId(e.getApsWorkDetail().getStationGroupId());
                    calendar.setStationId(e.getApsWorkDetail().getStationId());
                    calendar.setState(1); // 3 已排产
                    calendar.setOrderType(orderCache.getOrderIdMap().get(e.getApsWorkOrder().getOrderId()).getOrderType());
                    return calendar;
                }))
                .collect(Collectors.toList());
        apsDetailWeekdayMapper.insertBatch(apsDetailWeekdayList, 2000);

        Map<Integer, List<ApsWorkOrderTemp>> collect1 = orderCache.getApsWorkOrderTempList().stream().collect(Collectors.groupingBy(e -> e.getApsWorkOrder().getApsOrderId()));
        // 更新 apsOrder
        for (ApsOrder apsOrder : orderCache.getApsOrderList()) {
            List<ApsWorkOrderTemp> apsWorkOrderTemps = collect1.get(apsOrder.getId());
            ApsWorkOrderTemp min = apsWorkOrderTemps.stream().min(Comparator.comparing(e -> e.getApsWorkOrder().getPlanStartDateTime())).get();
            ApsWorkOrderTemp max = apsWorkOrderTemps.stream().max(Comparator.comparing(e -> e.getApsWorkOrder().getPlanEndDateTime())).get();
            apsOrder.setOrderStatus(5);
            apsOrder.setPlanStartDateTime(min.getApsWorkOrder().getPlanStartDateTime());
            apsOrder.setPlanEndDateTime(max.getApsWorkOrder().getPlanEndDateTime());
        }
        apsOrderMapper.updateBatchById(orderCache.getApsOrderList(), 500);

        // 更新 order
        for (Order order : orderCache.getOrderList()) {
            List<ApsOrder> apsOrders = orderCache.getApsOrderOrderIdMap().get(order.getId());
            ApsOrder min = apsOrders.stream().min(Comparator.comparing(e -> e.getPlanStartDateTime())).get();
            ApsOrder max = apsOrders.stream().min(Comparator.comparing(e -> e.getPlanEndDateTime())).get();
            order.setPlanStartDateTime(min.getPlanStartDateTime());
            order.setPlanEndDateTime(max.getPlanEndDateTime());
            order.setOrderStatus(5);
        }
        orderMapper.updateBatchById(orderCache.getOrderList(), 100);
    }


    public void scheduleTask(PlanFactoryCache factoryCache, PlanOrderCache orderCache) {

        // 找到需要排产的包
        List<ApsOrder> apsOrderList = orderCache.getApsOrderIdMap()
                .values()
                .stream()
                .sorted((e1, e2) -> {

                    int cmp = 0;
                    // 一个批次订单的顺序
                    cmp = orderCache.getOrderIdMap().get(e1.getOrderId()).getSeq().compareTo(orderCache.getOrderIdMap().get(e2.getOrderId()).getSeq());
                    if (cmp != 0) {
                        return cmp;
                    }

                    // 相同批次对比包顺序
                    cmp = e1.getSeq().compareTo(e2.getSeq());
                    if (cmp != 0) {
                        return -cmp;
                    }
                    return cmp;
                })
                .collect(Collectors.toList());

        // 排产每个包
        Map<Integer, List<ApsWorkOrderTemp>> apsWorkOrderApsOrderMap = orderCache.getApsWorkOrderTempList().stream().collect(Collectors.groupingBy(e -> e.getApsWorkOrder().getApsOrderId()));
        Map<Integer, Map<Integer, List<ApsWorkOrderTemp>>> workOrderApsOrderIdPidMap = orderCache.getApsWorkOrderTempList().stream().filter(e -> e.getPid() != null).collect(Collectors.groupingBy(e -> e.getApsWorkOrder().getApsOrderId(), Collectors.groupingBy(ApsWorkOrderTemp::getPid)));
        Map<Integer, Map<Integer, Map<Integer, List<ApsTask>>>> apsTaskWorkOrderIdPhaseSeqMap = orderCache.getTaskList().stream().collect(Collectors.groupingBy(e -> e.getApsWorkOrder().getApsOrderId(), Collectors.groupingBy(e -> e.getApsWorkDetail().getApsWorkOrderId(), Collectors.groupingBy(e -> e.getApsWorkDetail().getPhaseSeq()))));

        List<String> lines = new ArrayList<>();
        for (ApsOrder apsOrder : apsOrderList) {
            // 循环处理每个包的工单
            List<ApsWorkOrderTemp> apsWorkOrderTemps = apsWorkOrderApsOrderMap.get(apsOrder.getId());
            if (CollUtil.isEmpty(apsWorkOrderTemps)) {
                continue;
            }

            List<ApsWorkOrderTemp> parentWorkOrder = apsWorkOrderTemps.stream().filter(e -> e.getPid() == null).collect(Collectors.toList());

            while (!CollUtil.isEmpty(parentWorkOrder)) {


                List<ApsWorkOrderTemp> temp = new ArrayList<>();

                Map<String, Map<String, Integer>> phaseGroupWorkTimeMap = calcPhaseGroupWorkTimeMap(parentWorkOrder, apsOrder, orderCache, factoryCache, apsTaskWorkOrderIdPhaseSeqMap, lines);


                for (ApsWorkOrderTemp workOrderTemp : parentWorkOrder) {

                    phaseList(workOrderTemp, apsOrder, orderCache, factoryCache, apsTaskWorkOrderIdPhaseSeqMap, lines, phaseGroupWorkTimeMap);

                    List<ApsWorkOrderTemp> children = workOrderApsOrderIdPidMap.get(apsOrder.getId()).get(workOrderTemp.getId());
                    if (CollUtil.isEmpty(children)) {
                        continue;
                    }
                    temp.addAll(children);
                }

                parentWorkOrder = temp;
            }

            for (ApsMachine machine : factoryCache.getMachines()) {
                machine.setNextTask(null);
            }

        }

    }

    public Map<String, Map<String, Integer>> calcPhaseGroupWorkTimeMap(List<ApsWorkOrderTemp> parentWorkOrder, ApsOrder apsOrder, PlanOrderCache orderCache, PlanFactoryCache factoryCache, Map<Integer, Map<Integer, Map<Integer, List<ApsTask>>>> apsTaskWorkOrderIdPhaseSeqMap, List<String> lines) {
        Integer level = parentWorkOrder.get(0).getApsWorkOrder().getLevel();
        List<ApsWorkOrderTemp> collect = parentWorkOrder.stream().filter(e -> e.getApsWorkOrder().getLevel().equals(level)).collect(Collectors.toList());

        Map<String, Map<String, Integer>> phaseGroupWorkTimeMap = new HashMap<>();

        for (ApsWorkOrderTemp apsWorkOrderTemp : collect) {
//            phaseGroupWorkTimeMap.put(apsWorkOrderTemp.getApsWorkOrder().getPhaseCode(), new HashMap<>());

            Map<Integer, List<ApsTask>> phaseSeqMap = apsTaskWorkOrderIdPhaseSeqMap.get(apsWorkOrderTemp.getApsWorkOrder().getApsOrderId()).get(apsWorkOrderTemp.getId());
            if (CollUtil.isEmpty(phaseSeqMap) && apsWorkOrderTemp.getApsWorkOrder().getWorkChain().equals("WL")) {
                continue;
            }
            if (phaseSeqMap == null) {
                throw SyExceptionUtils.e("工单号：{}，PLMID：{}，物料号：{} 没有可执行工序", apsWorkOrderTemp.getApsWorkOrder().getWorkOrderCode(), apsWorkOrderTemp.getApsWorkOrder().getPlmId(), apsWorkOrderTemp.getApsWorkOrder().getPlmId());
            }
            List<Integer> phaseSeqKey = new ArrayList<>(phaseSeqMap.keySet());
            phaseSeqKey.sort(Comparator.comparingInt(e -> -e));

            for (int i = 0; i < phaseSeqKey.size(); i++) {
                Integer phaseSeq = phaseSeqKey.get(i);
                List<ApsTask> taskList = phaseSeqMap.get(phaseSeq).stream().filter(e -> e.getApsWorkOrder().getApsOrderId().equals(apsOrder.getId())).collect(Collectors.toList());

                if (apsWorkOrderTemp.getApsWorkOrder().getMaterType().equals("Assembly")) {

//                    Integer newVal = phaseGroupWorkTimeMap.getOrDefault(taskList.get(0).getPhaseCode(), new HashMap<>())
//                            .compute(taskList.get(0).getGroupCode(), (k, v) -> taskList.get(0).getApsWorkDetail().getWorkTime());
//                            .compute(taskList.get(0).getGroupCode(), (k, v) -> (v == null) ? taskList.get(0).getApsWorkDetail().getWorkTime() : v + taskList.get(0).getApsWorkDetail().getWorkTime());

//                    phaseGroupWorkTimeMap.computeIfAbsent(taskList.get(0).getPhaseCode(), k -> new HashMap<>());
//                    phaseGroupWorkTimeMap.get(taskList.get(0).getPhaseCode()).put(taskList.get(0).getGroupCode(), newVal);

                } else {
                    Integer reduce = taskList.stream()
                            .map(e -> e.getApsWorkDetail().getWorkTime() * e.getApsWorkDetail().getTotalCount().intValue())
                            .reduce(Integer::sum)
                            .get();

                    Integer newVal = phaseGroupWorkTimeMap.getOrDefault(taskList.get(0).getPhaseCode(), new HashMap<>())
                            .compute(taskList.get(0).getGroupCode(), (k, v) -> (v == null) ? reduce : v + reduce);
                    phaseGroupWorkTimeMap.computeIfAbsent(taskList.get(0).getPhaseCode(), k -> new HashMap<>());
                    phaseGroupWorkTimeMap.get(taskList.get(0).getPhaseCode()).put(taskList.get(0).getGroupCode(), newVal);
                }
            }
        }

        return phaseGroupWorkTimeMap;
    }


    public void phaseList(ApsWorkOrderTemp workOrderTemp, ApsOrder apsOrder, PlanOrderCache orderCache, PlanFactoryCache factoryCache, Map<Integer, Map<Integer, Map<Integer, List<ApsTask>>>> apsTaskWorkOrderIdPhaseSeqMap, List<String> lines, Map<String, Map<String, Integer>> phaseGroupWorkTimeMap) {

        Map<Integer, List<ApsTask>> phaseSeqMap = apsTaskWorkOrderIdPhaseSeqMap.get(workOrderTemp.getApsWorkOrder().getApsOrderId()).get(workOrderTemp.getId());

        if (CollUtil.isEmpty(phaseSeqMap) && workOrderTemp.getApsWorkOrder().getWorkChain().equals("WL")) {
            LocalDateTime start = orderCache.getApsWorkOrderTempIdMap().get(workOrderTemp.getPid()).getApsWorkOrder().getPlanStartDateTime();

//            PhaseExt phaseExt = factoryCache.getPhaseExtMap().get("WL");
            Phase phase = factoryCache.getPhaseCodeMap().get("WL");
            if (phase == null) {
                workOrderTemp.getApsWorkOrder().setPlanStartDateTime(start);
                workOrderTemp.getApsWorkOrder().setPlanEndDateTime(start);
            } else {
                workOrderTemp.getApsWorkOrder().setPlanEndDateTime(start);
                workOrderTemp.getApsWorkOrder().setPlanStartDateTime(start.plusSeconds(TaskTimeUtils.calcWlTime(phase)));
            }

            /*
            log.info(
                String.format("包号:%s, 零件名称:%s, PLMID:%s, 工序:%s, 数量:%s",
                        PkgUtils.gen(orderCache.getApsOrderIdMap().get(workOrderTemp.getApsWorkOrder().getApsOrderId()).getSeq()),
                        workOrderTemp.getApsWorkOrder().getMaterName(),
                        workOrderTemp.getApsWorkOrder().getPlmId(),
                        "无工序",
                        workOrderTemp.getApsWorkOrder().getOrderCount().toString()
                )
            );
             */
            return;
        }


        List<Integer> phaseSeqKey = new ArrayList<>(phaseSeqMap.keySet());
        phaseSeqKey.sort(Comparator.comparingInt(e -> -e));

        for (int i = 0; i < phaseSeqKey.size(); i++) {
            Integer phaseSeq = phaseSeqKey.get(i);
            List<ApsTask> taskList = phaseSeqMap.get(phaseSeq);
            if (i == 0 && workOrderTemp.getApsWorkOrder().getLevel().equals(1)) {
                // 如果没有顶层，则取包的结束时间
                LocalDate deliveryDate = orderCache.getApsOrderIdMap().get(workOrderTemp.getApsWorkOrder().getApsOrderId()).getDeliveryDate();
//                List<ApsMachine> apsMachines = factoryCache.getGroupCodeMachineMap().get(taskList.get(0).getGroupCode());
//                ApsWeekDay apsWeekDay = apsMachines.stream().flatMap(e -> e.getWeekDays().stream()).filter(e -> e.getShiftDay().equals(deliveryDate)).max(Comparator.comparing(ApsWeekDay::getEndDateTime)).get();
                for (int i1 = 0; i1 < taskList.size(); i1++) {
//                    taskList.get(i1).getApsWorkDetail().setPlanEndDateTime(apsWeekDay.getEndDateTime());
                    taskList.get(i1).getApsWorkDetail().setPlanEndDateTime(LocalDateTime.of(deliveryDate, LocalTime.of(0, 0, 0, 0)));
                }

            } else if (i == 0 && !workOrderTemp.getApsWorkOrder().getLevel().equals(1)) {
                // 获取顶层的工单时间
                Map<Integer, List<ApsTask>> parentPhaseSeqMap = apsTaskWorkOrderIdPhaseSeqMap.get(workOrderTemp.getApsWorkOrder().getApsOrderId()).get(workOrderTemp.getPid());
                List<ApsTask> parentTaskList = parentPhaseSeqMap.get(new LinkedList<>(parentPhaseSeqMap.keySet()).getFirst());
                int partCount = workOrderTemp.getApsWorkOrder().getPartCount().intValue();
                List<ApsTask> collect = parentTaskList.stream()
                        .sorted(Comparator.comparing((ApsTask e) -> e.getApsWorkDetail().getPlanStartDateTime()).reversed())
                        .flatMap(e -> Stream.iterate(e, e1 -> e1).limit(partCount))
                        .collect(Collectors.toList());

                int last = 0;
                for (ApsTask task : taskList) {
                    BigDecimal totalCount = task.getApsWorkDetail().getTotalCount();
                    Optional<LocalDateTime> min = collect.stream().map(e -> e.getApsWorkDetail().getPlanStartDateTime()).skip(last).limit(totalCount.intValue()).min(Comparator.comparing(e -> e));
                    if (min.isPresent()) {
                        task.getApsWorkDetail().setPlanEndDateTime(min.get());
                        last += totalCount.intValue();
                    }

                }

            } else {
                // 获取前一道工序的时间
                List<ApsTask> preTask = phaseSeqMap.get(phaseSeqKey.get(i - 1));
                ApsTask apsTask = preTask.stream().min(Comparator.comparing((ApsTask e) -> e.getApsWorkDetail().getPlanStartDateTime())).get();

                for (ApsTask task : taskList) {
                    task.getApsWorkDetail().setPlanEndDateTime(apsTask.getPlanStartDateTime());
                }

            }

            List<ApsMachine> machines = factoryCache.getGroupCodeMachineMap().get(taskList.get(0).getGroupCode());
            if (CollUtil.isEmpty(machines)) {
                throw SyExceptionUtils.e("没有找到工作组:[" + factoryCache.getGroupCodeMachineMap().get(taskList.get(0).getGroupCode() + "], 对应的工序:[" + factoryCache.getGroupCodeMachineMap().get(taskList.get(0).getPhaseCode() + "]")));
            }

            for (ApsTask currentTask : taskList) {
                List<ApsDetailWeekdayWrap> l = new ArrayList<>();
                LocalDateTime start = currentTask.getApsWorkDetail().getPlanEndDateTime();
                currentTask.getApsWorkDetail().setPlanEndDateTime(null);

                for (ApsMachine machine : machines) {

//                    if (workOrderTemp.getPid() != null && i == 0  ) {
//                        PhaseExt phaseExt = factoryCache.getPhaseExtMap().get(currentTask.getApsWorkDetail().getPhaseCode().replaceAll("\\d+$", ""));
//                        if(phaseExt != null){
//                            start = start.minusHours(phaseExt.getFlowTime().intValue());
//                        }
//
////                        if((orderCache.getApsWorkOrderTempIdMap().get(workOrderTemp.getPid()).getApsWorkOrder().getPlmId().matches(assPlmIdPattern) && !workOrderTemp.getApsWorkOrder().getPlmId().matches(assPlmIdPattern))
////                           ||
////                           (orderCache.getApsWorkOrderTempIdMap().get(workOrderTemp.getPid()).getApsWorkOrder().getMaterName().matches(assNamePattern) && !workOrderTemp.getApsWorkOrder().getMaterName().matches(assNamePattern))
////                           ){
////                            Map<Integer, List<ApsTask>> parentPhaseSeqMap = apsTaskWorkOrderIdPhaseSeqMap.get(workOrderTemp.getApsWorkOrder().getApsOrderId()).get(workOrderTemp.getPid());
////                            List<ApsTask> parentTaskList = parentPhaseSeqMap.get(new LinkedList<>(parentPhaseSeqMap.keySet()).getFirst());
////                            LocalDateTime tempStart = start;
////                            LocalDateTime localDateTime = parentTaskList.stream().min(Comparator.comparing(ApsTask::getPlanStartDateTime)).map(ApsTask::getPlanStartDateTime).orElse(tempStart);
////                            start = localDateTime.minusHours(hours);
////                        }
//
//                    }

                    ApsDetailWeekdayWrap scheduling = PlanTaskUtils.scheduling(factoryCache, orderCache, start, machine, currentTask, apsDetailWeekdayMapper, apsWorkDetailMapper, l, machines, phaseGroupWorkTimeMap);
                    l.add(scheduling);
                }
                ApsDetailWeekdayWrap calendarWrap = l.stream().max(Comparator.comparing(ApsDetailWeekdayWrap::getStartDateTime)).orElseThrow(() -> SyExceptionUtils.e("排产异常"));
                currentTask.getApsWorkDetail().setPlanStartDateTime(calendarWrap.getStartDateTime());
                currentTask.getApsWorkDetail().setPlanEndDateTime(calendarWrap.getEndDateTime());
                currentTask.setNextTask(calendarWrap.getNext());
                calendarWrap.getPrevious().setNextTask(currentTask);
                currentTask.getApsWorkDetail().setStationId(calendarWrap.getMachine().getId());
                currentTask.setStartMachineCalendar(calendarWrap.getStartMachineCalendar());
                currentTask.setAll(calendarWrap.getAll());
                currentTask.getApsWorkDetail().setStatus(2);
                currentTask.setStatus(5);

                calendarWrap.getMachine().setNextTask(null);

                /*
                log.info(String.format("包号:%s, 零件名称:%s, PLMID:%s, 工序:%s, 开始时间:%s, 结束时间:%s, 工位:%s, 工作组:%s, 数量:%s",
                        PkgUtils.gen(orderCache.getApsOrderIdMap().get(currentTask.getApsWorkDetail().getApsOrderId()).getSeq()),
                        currentTask.getApsWorkDetail().getMaterName(),
                        currentTask.getApsWorkDetail().getPlmId(),
                        currentTask.getApsWorkDetail().getPhaseCode(),
                        MkDateTimeUtils.toString(currentTask.getApsWorkDetail().getPlanStartDateTime()),
                        MkDateTimeUtils.toString(currentTask.getApsWorkDetail().getPlanEndDateTime()),
                        currentTask.getApsWorkDetail().getStationId(),
                        currentTask.getApsWorkDetail().getGroupCode(),
                        currentTask.getApsWorkDetail().getTotalCount().intValue()));
                 */
            }

        }

//        List<Integer> phaseSeqKey = phaseSeqMap.keySet().stream().sorted(Comparator.comparingInt((Integer e) -> e).reversed()).collect(Collectors.toList());
//        LocalDateTime start = null;
//        for (int i = 0; i < phaseSeqKey.size(); i++) {
//            if (i == 0) {
//                start = apsTaskWorkOrderIdPhaseSeqMap.get(workOrderTemp.getPid()).get(phaseSeqKey.get(i)).get(0).getApsWorkDetail().getPlanStartDateTime();
//            } else {
//                start = orderCache.getApsWorkOrderTempIdMap().get(workOrderTemp.getApsWorkOrder().getPid()).getApsWorkOrder().getPlanStartDateTime();
//            }
//        }

        ApsTask minTask = phaseSeqMap.values().stream().flatMap(List::stream).min(Comparator.comparing(ApsTask::getApsWorkDetail, Comparator.comparing(ApsWorkDetail::getPlanStartDateTime))).get();
        ApsTask maxTask = phaseSeqMap.values().stream().flatMap(List::stream).max(Comparator.comparing(ApsTask::getApsWorkDetail, Comparator.comparing(ApsWorkDetail::getPlanEndDateTime))).get();
        workOrderTemp.getApsWorkOrder().setPlanStartDateTime(minTask.getPlanStartDateTime());
        workOrderTemp.getApsWorkOrder().setPlanEndDateTime(maxTask.getPlanEndDateTime());

    }


    public void initOrderCache(PlanFactoryCache factoryCache, PlanOrderCache orderCache) {

        // 初始化排产批次信息
        LocalDateTime now = LocalDateTime.now();
        orderCache.getApsBatch().setStatus(1);
        orderCache.getApsBatch().setBatchNo(now.format(DatePattern.NORM_DATETIME_FORMATTER));
        orderCache.getApsBatch().setSchedulingDateTime(now);
        apsBatchMapper.updateById(orderCache.getApsBatch());

        for (Order order : orderCache.getOrderList()) {
            order.setPlanStartDateTime(null);
            order.setPlanEndDateTime(null);
            order.setBatchId(orderCache.getApsBatch().getId());
            orderMapper.updateById(order);
        }

        // 初始化所有订单 BOM 的 List 和 Map
        List<OrderBom> orderBomList = orderBomMapper.lambdaQuery().in(OrderBom::getOrderId, orderCache.getOrderIdMap().keySet()).list();
        orderCache.setOrderBomList(orderBomList);

        Map<Integer, List<OrderBom>> orderBomOrderIdMap = orderBomList.stream().collect(Collectors.groupingBy(OrderBom::getOrderId));
        orderCache.setOrderBomOrderIdMap(orderBomOrderIdMap);

        Map<Integer, OrderBom> orderBomIdMap = orderBomList.stream().collect(Collectors.toMap(OrderBom::getId, Function.identity()));
        orderCache.setOrderBomIdMap(orderBomIdMap);

        // 初始化所有订单 PHASE 的 List 和 Map
        List<OrderPhase> orderPhaseList = orderPhaseMapper.lambdaQuery().in(OrderPhase::getOrderId, orderCache.getOrderIdMap().keySet()).list();
        orderCache.setOrderPhaseList(orderPhaseList);

        Map<Integer, List<OrderPhase>> orderPhaseOrderIdMap = orderPhaseList.stream().collect(Collectors.groupingBy(OrderPhase::getOrderId));
        orderCache.setOrderPhaseOrderIdMap(orderPhaseOrderIdMap);

        Map<Integer, OrderPhase> orderPhaseIdMap = orderPhaseList.stream().collect(Collectors.toMap(OrderPhase::getId, Function.identity()));
        orderCache.setOrderPhaseIdMap(orderPhaseIdMap);

        // 初始化所有包的 ApsWorkOrder
        Map<Integer, List<ApsOrder>> apsOrderOrderIdMap = orderCache.getApsOrderOrderIdMap();
        List<ApsWorkOrder> apsWorkOrderList = orderCache.getOrderBomList()
                .stream()
                .map(e -> {
                    ApsWorkOrder apsBom = MkBeanUtils.copyProperties(e, ApsWorkOrder.class);
                    apsBom.setBomId(e.getId());
                    apsBom.setBomPid(e.getPid());
                    apsBom.setId(null);
                    apsBom.setPid(null);
                    apsBom.setStatus(1);
                    apsBom.setFinishCount(new BigDecimal(0));
                    apsBom.setTotalCount(e.getTotalCount());
                    apsBom.setOrderType(orderCache.getOrderIdMap().get(e.getOrderId()).getOrderType());
                    apsBom.setPlmId(e.getPlmId());
//                    apsBom.setApsOrderId(apsOrder.getId());
//                    apsBom.setOrderCount(e.getTotalCount().multiply(new BigDecimal(apsOrder.getOrderQty())));
                    return apsBom;
                })
                .flatMap(e ->
                        apsOrderOrderIdMap.get(e.getOrderId())
                                .stream()
                                .map(e1 -> {
                                    ApsWorkOrder awo = new ApsWorkOrder();
                                    BeanUtils.copyProperties(e, awo);
                                    awo.setApsOrderId(e1.getId());
                                    awo.setOrderCount(e.getTotalCount().multiply(new BigDecimal(e1.getOrderQty())));
                                    return awo;
                                })
                )
//                .filter(e -> !(e.getErpCode().startsWith("010") || e.getErpCode().startsWith("020") || e.getErpCode().startsWith("050")))
                .collect(Collectors.toList());


        // 构建 ApsWorkOrder 缓存对象
        List<ApsWorkOrderTemp> apsWorkOrderTempList = apsWorkOrderList.stream()
                .map(e -> {
                    ApsWorkOrderTemp apsWorkOrderTemp = new ApsWorkOrderTemp();
                    apsWorkOrderTemp.setApsWorkOrder(e);
                    return apsWorkOrderTemp;
                })
                .collect(Collectors.toList());

        Map<Integer, Map<Integer, List<ApsWorkOrderTemp>>> pidBomMap = apsWorkOrderTempList.stream()
                .filter(e -> e.getApsWorkOrder().getBomPid() != null)
                .collect(Collectors.groupingBy(e -> e.getApsWorkOrder().getApsOrderId(), Collectors.groupingBy(e -> e.getApsWorkOrder().getBomPid())));
        List<ApsWorkOrderTemp> parentBoms = apsWorkOrderTempList.stream().filter(e -> e.getApsWorkOrder().getBomPid() == null).collect(Collectors.toList());
        int workOrderId = 1;
        if (CollUtil.isNotEmpty(parentBoms)) {
            for (ApsWorkOrderTemp parentBom : parentBoms) {
                parentBom.setId(workOrderId++);
            }
        }
        while (CollUtil.isNotEmpty(parentBoms)) {

            List<ApsWorkOrderTemp> temp = new ArrayList<>();
            for (ApsWorkOrderTemp parentBom : parentBoms) {
                List<ApsWorkOrderTemp> apsBomChildren = pidBomMap.get(parentBom.getApsWorkOrder().getApsOrderId()).get(parentBom.getApsWorkOrder().getBomId());
                if (CollUtil.isEmpty(apsBomChildren)) {
                    continue;
                }

                if (parentBom.getApsWorkOrder().getWorkChain().equals("WL")) {
                    continue;
                }


                for (ApsWorkOrderTemp apsBom : apsBomChildren) {
                    apsBom.setPid(parentBom.getId());
                }
                temp.addAll(apsBomChildren);

            }

            parentBoms = temp;
            if (CollUtil.isNotEmpty(temp)) {
                for (ApsWorkOrderTemp apsWorkOrderTemp : temp) {
                    apsWorkOrderTemp.setId(workOrderId++);
                }
            }
        }

        apsWorkOrderTempList.removeIf(e -> e.getId() == null);
        orderCache.setApsWorkOrderTempList(apsWorkOrderTempList);
        orderCache.setApsWorkOrderTempIdMap(orderCache.getApsWorkOrderTempList().stream().collect(Collectors.toMap(ApsWorkOrderTemp::getId, Function.identity())));
        orderCache.setApsWorkOrderTempPidMap(orderCache.getApsWorkOrderTempList().stream().filter(e -> e.getPid() != null).collect(Collectors.groupingBy(ApsWorkOrderTemp::getPid, Collectors.toList())));

        // 取出倒排表数据
        List<ReverseDetail> reverseDetails = reverseDetailMapper.lambdaQuery().in(ReverseDetail::getOrderId, orderCache.getOrderIdMap().keySet()).list();
        Map<String, ReverseDetail> collect1 = reverseDetails.stream().collect(Collectors.toMap(e -> e.getOrderId() + ":" + e.getBomId() + ":" + e.getPhaseId(), Function.identity(), (e1, e2) -> e1.getReverseEndTime().isBefore(e2.getReverseEndTime()) ? e1 : e2));
        reverseDetails = collect1.values().stream().sorted(Comparator.comparing(ReverseDetail::getReverseEndTime).thenComparing(ReverseDetail::getPhaseId)).collect(Collectors.toList());
        Map<Integer, List<ReverseDetail>> reverseDetailIdMap = reverseDetails.stream().collect(Collectors.groupingBy(ReverseDetail::getOrderId));
        Map<Integer, Map<Integer, ReverseDetail>> collect = reverseDetails.stream().collect(Collectors.groupingBy(ReverseDetail::getOrderId, Collectors.toMap(ReverseDetail::getPhaseId, Function.identity())));

        orderCache.setReverseDetailList(reverseDetails);
        orderCache.setReverseDetailOrderIdMap(reverseDetailIdMap);
        orderCache.setReverseDetailOrderIdPhaseIdMap(collect);

        // 构建 ApsWorkDetail 缓存对象
        Map<Integer, List<ApsWorkOrderTemp>> apsWorkOrderTempBomIdMap = apsWorkOrderTempList.stream().collect(Collectors.groupingBy(e -> e.getApsWorkOrder().getBomId()));

        List<ApsWorkDetailTemp> apsWorkDetailTempList = new ArrayList<>();
        for (int i = 0; i < reverseDetails.size(); i++) {
            ReverseDetail e = reverseDetails.get(i);

            ApsWorkDetail apsWorkDetail = new ApsWorkDetail();
            apsWorkDetail.setOrderId(e.getOrderId());

            OrderPhase orderPhase = orderPhaseIdMap.get(e.getPhaseId());

            if (factoryCache.getIgnorePhaseCode().contains(orderPhase.getPhaseCode())) {
                continue;
            }

            apsWorkDetail.setPhaseId(orderPhase.getId());
            apsWorkDetail.setWorkTime(orderPhase.getWorkTime());

            apsWorkDetail.setPhaseSeq(orderPhase.getPhaseSeq());
            apsWorkDetail.setPhaseCode(orderPhase.getPhaseCode());
            apsWorkDetail.setPhaseName(orderPhase.getPhaseName());
            apsWorkDetail.setPhaseCode(orderPhase.getPhaseCode());
            apsWorkDetail.setMaterNo(orderPhase.getMaterNo());
            apsWorkDetail.setMaterName(orderPhase.getMaterName());
            apsWorkDetail.setGroupCode(orderPhase.getWorkGroupNumber());
            apsWorkDetail.setOrderType(orderCache.getOrderIdMap().get(orderPhase.getOrderId()).getOrderType());

            // 从倒排表里取
            apsWorkDetail.setPartCount(orderCache.getOrderBomIdMap().get(e.getBomId()).getPartCount());
            apsWorkDetail.setGroupId(e.getGroupId());
            apsWorkDetail.setProcessCount(new BigDecimal(0));
            apsWorkDetail.setFinishCount(new BigDecimal(0));
            apsWorkDetail.setStatus(1);
            apsWorkDetail.setStationGroupId(e.getStationGroupId());

            apsWorkDetail.setDetailSeq(i + 1);
            apsWorkDetail.setReverseDetailId(e.getId());
            apsWorkDetail.setType(2);
            apsWorkDetail.setOp(orderPhase.getOp());
            apsWorkDetail.setOpCode(orderPhase.getOpCode());


            List<ApsWorkOrderTemp> apsWorkOrders = apsWorkOrderTempBomIdMap.get(orderPhase.getBomId());

            for (int i1 = 0; i1 < apsWorkOrders.size(); i1++) {
                ApsWorkOrderTemp apsWorkOrder = apsWorkOrders.get(i1);
                apsWorkDetail.setPlmId(apsWorkOrder.getApsWorkOrder().getPlmId()); // 注意，此处应该挪上面

                ApsWorkDetail awd = new ApsWorkDetail();
                BeanUtils.copyProperties(apsWorkDetail, awd);

                awd.setApsOrderId(apsWorkOrder.getApsWorkOrder().getApsOrderId());
                awd.setApsWorkOrderId(apsWorkOrder.getId());
                awd.setApsWorkOrderPid(apsWorkOrder.getPid());
                awd.setTotalCount(apsWorkOrder.getApsWorkOrder().getOrderCount());


                // 再计算转运数量
                List<Station> stationList = factoryCache.getMasterStationMap().get(e.getGroupCode());
                if (CollUtil.isEmpty(stationList)) {
                    throw SyExceptionUtils.e("{}没有工位", e.getGroupCode());
                }
                Integer smallTransfer = stationList.get(0).getSmallTransfer();
                if (smallTransfer == null || smallTransfer <= 0) {
                    ApsWorkDetailTemp temp = new ApsWorkDetailTemp();
                    temp.setApsWorkOrderTemp(apsWorkOrder);
                    temp.setApsWorkDetail(awd);
                    temp.setApsWorkOrder(apsWorkOrder.getApsWorkOrder());
                    apsWorkDetailTempList.add(temp);
                    continue;
                }

                int div = apsWorkOrder.getApsWorkOrder().getOrderCount().divide(new BigDecimal(smallTransfer), 0, RoundingMode.DOWN).intValue();

                BigDecimal subtract = apsWorkOrder.getApsWorkOrder().getOrderCount().subtract(new BigDecimal(smallTransfer).multiply(new BigDecimal(div)));

                for (int i2 = 0; i2 < div; i2++) {
                    ApsWorkDetail awd1 = MkBeanUtils.copyProperties(awd, ApsWorkDetail.class);
                    awd1.setTotalCount(new BigDecimal(smallTransfer));

                    ApsWorkDetailTemp temp = new ApsWorkDetailTemp();
                    temp.setApsWorkOrderTemp(apsWorkOrder);
                    temp.setApsWorkDetail(awd1);
                    temp.setApsWorkOrder(apsWorkOrder.getApsWorkOrder());
                    apsWorkDetailTempList.add(temp);
                }

                if (subtract.compareTo(BigDecimal.ZERO) > 0) {
                    ApsWorkDetail awd1 = MkBeanUtils.copyProperties(awd, ApsWorkDetail.class);
                    awd1.setTotalCount(subtract);

                    ApsWorkDetailTemp temp = new ApsWorkDetailTemp();
                    temp.setApsWorkOrderTemp(apsWorkOrder);
                    temp.setApsWorkDetail(awd1);
                    temp.setApsWorkOrder(apsWorkOrder.getApsWorkOrder());
                    apsWorkDetailTempList.add(temp);
                }

            }

        }

        int workDetailId = 1;
        for (ApsWorkDetailTemp apsWorkDetailTemp : apsWorkDetailTempList) {
            apsWorkDetailTemp.setId(workDetailId++);
        }

        List<ApsTask> tasks = apsWorkDetailTempList.stream()
                .map(e -> {
                    ApsTask t = new ApsTask(e.getApsWorkDetail());
                    t.setApsWorkOrderTemp(e.getApsWorkOrderTemp());
                    t.setApsWorkDetailTemp(e);
                    t.setApsWorkOrder(e.getApsWorkOrder());
                    t.setId(e.getApsWorkDetail().getId());
                    t.setConsumerStatus(1);
                    t.setConsumerCount(new BigDecimal(0));
                    t.setStatus(2);
                    return t;
                })
                .sorted(Comparator.comparing(t -> t.getApsWorkDetail().getDetailSeq()))
                .collect(Collectors.toList());
        orderCache.setTaskList(tasks);


        Map<Integer, Map<Integer, List<ApsTask>>> workOrderIdPhaseSeqMap = tasks.stream().collect(Collectors.groupingBy(e -> e.getApsWorkOrderTemp().getId(), Collectors.groupingBy(ApsTask::getPhaseSeq)));
        orderCache.setWorkOrderIdPhaseSeqMap(workOrderIdPhaseSeqMap);

        Map<Integer, List<ApsTask>> workOrderPidMap = tasks.stream().filter(e -> e.getApsWorkOrderPid() != null).collect(Collectors.groupingBy(ApsTask::getApsWorkOrderPid));
        orderCache.setWorkOrderPidMap(workOrderPidMap);

        for (Order order : orderCache.getOrderList()) {
            order.setPlanStartDateTime(null);
            order.setPlanEndDateTime(null);
        }

        for (ApsOrder apsOrder : orderCache.getApsOrderList()) {
            apsOrder.setPlanStartDateTime(null);
            apsOrder.setPlanEndDateTime(null);
        }

    }

    public void initFactoryCache(PlanFactoryCache factoryCache, PlanOrderCache orderCache) {

        // 设置工厂信息
        factoryCache.setFactoryCode(projectConig.getFactoryCode());

        // 设置客户信息
        List<Integer> customerList = orderCache.getApsOrderList().stream().map(ApsOrder::getCustomerId).distinct().collect(Collectors.toList());
        if (CollUtil.isNotEmpty(customerList)) {
            List<Customer> customers = customerMapper.selectBatchIds(customerList);
            factoryCache.setCustomers(customers);
        }

        // 工作组信息
        List<String> groupNumberString = orderPhaseMapper.selectGroupList(Wrappers.<OrderPhase>query()
                .in("order_id", orderCache.getOrderIdMap().keySet())
        );

        List<Integer> lineIds = orderCache.getOrderList().stream().map(Order::getLineId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        List<Group> groups = groupMapper.lambdaQuery().in(Group::getMyCode, groupNumberString).eq(Group::getType, 2).in(Group::getLineId, lineIds).list();
        factoryCache.setGroups(groups);

        // 工作组工位关联
//        List<StationGroup> stationGroups = stationGroupMapper.lambdaQuery().in(StationGroup::getGroupId, groups.stream().map(BaseEntity::getId).collect(Collectors.toList())).list();
//        factoryCache.setStationGroups(stationGroups);

        // 工位信息
//        List<Integer> stationIds = stationGroups.stream().map(StationGroup::getStationId).distinct().collect(Collectors.toList());
//        List<Station> stations = stationMapper.selectBatchIds(stationIds);
        List<Station> stations = stationMapper.lambdaQuery().in(Station::getGroupId, groups.stream().map(BaseEntity::getId).collect(Collectors.toList())).list();

        for (Station station : stations) {
            if (station.getEnterTime() == null || station.getEnterTime() < 0) {
                station.setEnterTime(0);
            }

            if (station.getOutTime() == null || station.getOutTime() < 0) {
                station.setOutTime(0);
            }

            if (station.getChangTime() == null || station.getChangTime() < 0) {
                station.setChangTime(0);
            }
        }

        factoryCache.setStations(stations);

        // 快捷从工作组找到主工位列表
        Map<Integer, List<Station>> stationGroupMap = stations.stream().collect(Collectors.groupingBy(Station::getGroupId));
//        Map<Integer, Station> stationMap = stations.stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity()));
        Map<String, List<Station>> masterStationMap = new HashMap<>();
        for (Group group : groups) {
            List<Station> value = stationGroupMap.get(group.getId());
//            if (CollUtil.isEmpty(stationGroups1)) {
//                continue;
//            }

//            List<Station> value = stationGroupMap.get(group.getId())
//                    .stream()
//                    .map(e -> stationMap.get(e.getStationId()))
//                    .collect(Collectors.toList());

            masterStationMap.put(group.getMyCode(), value);
        }
        factoryCache.setMasterStationMap(masterStationMap);

        // 工位日历
        List<Integer> shiftTypeIds = groups.stream().map(Group::getShiftTypeId).distinct().collect(Collectors.toList());
        List<ShiftType> shiftTypes = shiftTypeMapper.selectBatchIds(shiftTypeIds);

//        List<Integer> shiftTypeIds = shiftTypes.stream().map(ShiftType::getId).distinct().collect(Collectors.toList());
        List<Shift> shifts = shiftMapper.lambdaQuery().in(Shift::getShiftTypeId, shiftTypeIds).list();

        List<WeekdayRule> weekdayRules = weekdayRuleMapper.lambdaQuery().in(WeekdayRule::getShiftTypeId, shiftTypeIds).list();

        List<BreakPeriod> breakPeriods = breakPeriodMapper.lambdaQuery().in(BreakPeriod::getShiftId, shifts.stream().map(BaseEntity::getId).distinct().collect(Collectors.toList())).list();
        Map<Integer, Shift> shiftIdMap = shifts.stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity()));

//        factoryCache.setStationWeekdaySchemes(stationWeekdaySchemes);
        factoryCache.setStationWeekdayRules(weekdayRules);
        factoryCache.setShiftTypes(shiftTypes);
        factoryCache.setShifts(shifts);
        factoryCache.setBreakPeriods(breakPeriods);

        // 拼装设备时间链
        LocalDate startDate = orderCache.getReverseOrderList().stream().min(Comparator.comparing(ReverseOrder::getDeliveryDate)).orElseThrow(() -> SyExceptionUtils.e("没有最小的交付日期")).getDeliveryDate();
        LocalDate endDate = orderCache.getReverseOrderList().stream().max(Comparator.comparing(ReverseOrder::getDeliveryDate)).orElseThrow(() -> SyExceptionUtils.e("没有最大的交付日期")).getDeliveryDate();

        // 初始化日历
        factoryCache.setMachines(stations.stream().map(ApsMachine::new).collect(Collectors.toList()));
        factoryCache.initDay(startDate, endDate);
        for (ApsMachine machine : factoryCache.getMachines()) {
            Integer totalWorkTime = machine.getWeekDays()
                    .stream()
                    .map(e -> shiftIdMap.get(e.getShiftId()).getTotalWorkTime())
                    .reduce(0, Integer::sum);

            Integer validTime = machine.getWeekDays()
                    .stream()
                    .map(e -> shiftIdMap.get(e.getShiftId()).getValidTime())
                    .reduce(0, Integer::sum);

            machine.setTotalWorkTime(totalWorkTime);
            machine.setValidTime(validTime);

        }


        // 生产工单占位填充 未编写,领导要求无限产能

        // 工作组映射关系填充
        Map<String, List<ApsMachine>> groupCodeMachineMap = new HashMap<>();
        Map<Integer, ApsMachine> idApsMachineMap = factoryCache.getMachines().stream().collect(Collectors.toMap(ApsMachine::getId, Function.identity()));
        for (Map.Entry<String, List<Station>> entry : masterStationMap.entrySet()) {
            List<Station> collect = entry.getValue().stream().filter(e -> e.getWorkAbility() == null).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(collect)) {
                throw SyExceptionUtils.e("工位{}没有工作能力", entry.getValue().get(0).getName());
            }

            List<Station> collect1 = entry.getValue().stream().filter(e -> e.getProductivity() == null).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(collect1)) {
                throw SyExceptionUtils.e("工位{}没有生产效率", entry.getValue().get(0).getName());
            }
            groupCodeMachineMap.put(entry.getKey(), entry.getValue().stream().map(e -> idApsMachineMap.get(e.getId())).collect(Collectors.toList()));
        }

        factoryCache.setGroupCodeMachineMap(groupCodeMachineMap);

        factoryCache.setIgnorePhaseCode(projectConig.getIgnorePhaseCodeList());

        factoryCache.setPzCode(WorkshopProp.PZ_PHASE_CODE);

        factoryCache.setPzSecond(WorkshopProp.PZ_SECOND);

//        List<PhaseExt> phaseExtList = phaseExtMapper.lambdaQuery().list();
//        factoryCache.setPhaseExtList(phaseExtList);
//        factoryCache.setPhaseExtMap(phaseExtList.stream().collect(Collectors.toMap(PhaseExt::getPhaseCode, Function.identity())));

        List<Phase> phaseList = phaseMapper.lambdaQuery().list();
        Map<String, Phase> phaseCodeMap = phaseList.stream().collect(Collectors.toMap(Phase::getMyCode, Function.identity()));
        factoryCache.setPhaseCodeMap(phaseCodeMap);

    }

    public void initOrderAndApsOrder(PlanSchedulingReqVo reqVo, PlanOrderCache orderCache) {
        // order
        List<Order> orderList = orderMapper.lambdaQuery()
                .in(Order::getOrderType, ApsConstant.ORDER_TYPE_MAP.get(orderCache.getSchedulingSource()))
                .in(Order::getOrderStatus, 3, 4, 5)
                .list();

        if (CollUtil.isEmpty(orderList)) {
            throw SyExceptionUtils.e("没有待排产的订单");
        }

        long count = orderList.stream().map(Order::getLineId).distinct().count();
        if (count > 1) {
            throw SyExceptionUtils.e("多个产线的订单不得混排");
        }


        orderCache.setOrderList(orderList);
        orderCache.setOrderIdMap(orderList.stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity())));
        orderCache.setSchedulingRule(reqVo.getSchedulingRule());

        // apsOrder
        List<Integer> orderIds = orderList.stream().map(BaseEntity::getId).collect(Collectors.toList());
        List<ApsOrder> apsOrderList = apsOrderMapper.lambdaQuery()
                .in(ApsOrder::getOrderId, orderIds)
                .in(ApsOrder::getOrderType, ApsConstant.ORDER_TYPE_MAP.get(orderCache.getSchedulingSource()))
                .list();
        orderCache.setApsOrderList(apsOrderList);
        orderCache.setApsOrderIdMap(apsOrderList.stream().collect(Collectors.toMap(ApsOrder::getId, Function.identity())));
        Map<Integer, List<ApsOrder>> orderMap = orderCache.getApsOrderList().stream().sorted(Comparator.comparing(ApsOrder::getSeq)).collect(Collectors.groupingBy(ApsOrder::getOrderId, LinkedHashMap::new, Collectors.toList()));
        orderCache.setApsOrderOrderIdMap(orderMap);


        // reverseOrder
        List<ReverseOrder> reverseOrderList = reverseOrderMapper.lambdaQuery()
                .in(ReverseOrder::getOrderId, orderIds)
                .isNotNull(ReverseOrder::getParentId)
                .list();

        orderCache.setReverseOrderList(reverseOrderList);
        orderCache.setReverseOrderOrderIdMap(reverseOrderList.stream().collect(Collectors.groupingBy(ReverseOrder::getOrderId)));

    }


    public void clear(PlanOrderCache orderCache) {
        for (Map.Entry<Integer, List<ApsOrder>> entry : orderCache.getApsOrderOrderIdMap().entrySet()) {
            LambdaUpdateWrapper<ApsOrder> wr = Wrappers.lambdaUpdate();
            wr.set(ApsOrder::getPlanStartDateTime, null);
            wr.set(ApsOrder::getPlanEndDateTime, null);
            wr.in(ApsOrder::getId, entry.getValue().stream().map(BaseEntity::getId).collect(Collectors.toList()));
            apsOrderMapper.update(null, wr);

            apsWorkOrderMapper.deleteOrderId(entry.getKey());
            apsWorkDetailMapper.deleteOrderId(entry.getKey());
            apsDetailWeekdayMapper.deleteOrderId(entry.getKey());
        }

    }

    public void lock(PlanOrderCache orderCache) {

        // 1.把待排产的订单号放入redis，设置锁定状态
        ValueOperations<String, String> valOperation = stringRedisTemplate.opsForValue();
        String redisVal = orderCache.getOrderList().stream().map(e -> e.getConstructNo() + "_" + e.getMaterName()).collect(Collectors.joining(","));
        valOperation.set(ApsConstant.APS_SCHEDULING_LOCK, redisVal, 1, TimeUnit.DAYS);

        // 2.准备批次数据
        ApsBatch apsBatch = apsBatchMapper.lambdaQuery().eq(ApsBatch::getStatus, 1).one();
        if (apsBatch == null) {
            apsBatch = genBatch(orderCache);
            apsBatchMapper.insert(apsBatch);
        } else {
            modifyBatch(apsBatch, orderCache);
            apsBatchMapper.updateById(apsBatch);
        }
        orderCache.setApsBatch(apsBatch);

        List<Integer> apsOrderIds = orderCache.getApsOrderList().stream().map(BaseEntity::getId).collect(Collectors.toList());
        apsOrderMapper.lambdaUpdate()
                .set(ApsOrder::getOrderStatus, 4)
                .in(ApsOrder::getId, apsOrderIds)
                .update();

    }

    public void valid(PlanOrderCache orderCache) {

        // 如果有排产中的订单，则提示报错
        long count = orderCache.getOrderList().stream().filter(e -> e.getOrderStatus().equals(4)).count();
        if (count > 0) {
            throw SyExceptionUtils.e("有排产中的订单，请稍后再试");
        }

        // 取出所有待排产的订单
        if (CollUtil.isEmpty(orderCache.getOrderList())) {
            throw SyExceptionUtils.e("没有待排产的订单");
        }

        // 检查redis是否有锁定状态，如果有则提示，并且不排产
        ValueOperations<String, String> valOperation = stringRedisTemplate.opsForValue();
        String lockFlag = valOperation.get(ApsConstant.APS_SCHEDULING_LOCK);
        if (StringUtils.hasText(lockFlag)) {
            throw SyExceptionUtils.e("{} 等订单排产中!", lockFlag);
        }

    }

    public void modifyBatch(ApsBatch apsBatch, PlanOrderCache reqVo) {
        LocalDateTime now = LocalDateTime.now();
        apsBatch.setBatchNo(now.format(DatePattern.NORM_DATETIME_FORMATTER));
        apsBatch.setSchedulingDateTime(now);
        apsBatch.setSchedulingRule(reqVo.getSchedulingRule());
    }

    public ApsBatch genBatch(PlanOrderCache reqVo) {
        LocalDateTime now = LocalDateTime.now();
        ApsBatch apsBatch = new ApsBatch();
        apsBatch.setBatchNo(now.format(DatePattern.NORM_DATETIME_FORMATTER));
        apsBatch.setSchedulingRule(reqVo.getSchedulingRule());
        apsBatch.setSchedulingDateTime(now);
        apsBatch.setStatus(1);
        return apsBatch;
    }

    public void unlock(PlanSchedulingReqVo reqVo) {
//        ValueOperations<String, String> valOperation = stringRedisTemplate.opsForValue();
//        valOperation.(ApsConstant.APS_SCHEDULING_LOCK, redisVal, 1, TimeUnit.DAYS);
//        Long expire = stringRedisTemplate.getExpire(ApsConstant.APS_SCHEDULING_LOCK, TimeUnit.SECONDS);
        stringRedisTemplate.delete(ApsConstant.APS_SCHEDULING_LOCK);
//        if(stringRedisTemplate.hasKey(ApsConstant.APS_SCHEDULING_LOCK)){
//
//        }
    }

    public void unlock(PlanReSchedulingReqVo reqVo) {
        stringRedisTemplate.delete(ApsConstant.APS_SCHEDULING_LOCK);
    }

    @Transactional
    public void reScheduling(PlanReSchedulingReqVo reqVo) {
        log.info("scheduling reqVo: {}", JsonUtils.toJsonString(reqVo));

        PlanOrderCache orderCache = new PlanOrderCache();
        PlanFactoryCache factoryCache = new PlanFactoryCache();

        orderCache.setSchedulingSource("erp");

        initOrderAndApsOrder(reqVo, orderCache);

        valid(orderCache);

        lock(orderCache);

//        clear(orderCache);

        initFactoryCache(factoryCache, orderCache);

        initOrderCacheRe(factoryCache, orderCache);

        scheduleTask(factoryCache, orderCache);

        saveAndUpateRe(orderCache);
    }


    public void saveAndUpateRe(PlanOrderCache orderCache) {

        // 更新所有的apsWorkOrder
        for (ApsWorkOrderTemp apsWorkOrderTemp : orderCache.getApsWorkOrderTempList()) {
            ApsWorkOrder awo = new ApsWorkOrder();
            awo.setId(apsWorkOrderTemp.getApsWorkOrder().getId());
            awo.setPlanStartDateTime(apsWorkOrderTemp.getApsWorkOrder().getPlanStartDateTime());
            awo.setPlanEndDateTime(apsWorkOrderTemp.getApsWorkOrder().getPlanEndDateTime());
            awo.setModifyDateTime(LocalDateTime.now());
            apsWorkOrderMapper.updateById(awo);

        }

        // 保存所有的workDetail
        for (ApsTask task : orderCache.getTaskList()) {
            ApsWorkDetail awot = new ApsWorkDetail();

            ApsWorkDetailTemp a = task.getApsWorkDetailTemp();
            awot.setId(a.getApsWorkDetail().getId());
            awot.setPlanStartDateTime(a.getApsWorkDetail().getPlanStartDateTime());
            awot.setPlanEndDateTime(a.getApsWorkDetail().getPlanEndDateTime());
            awot.setModifyDateTime(LocalDateTime.now());
            apsWorkDetailMapper.updateById(awot);
        }

        List<Integer> apsWorkDetailIdList = orderCache.getTaskList().stream().map(e -> e.getApsWorkDetail().getId()).collect(Collectors.toList());
        QueryWrapper<ApsDetailWeekday> qw = new QueryWrapper<>();
        qw.in("aps_work_detail_id", apsWorkDetailIdList);
        apsDetailWeekdayMapper.deleteByApsWorkDetailId(qw);


        // 保存所有的apsDetailWeekday
        for (ApsTask task : orderCache.getTaskList()) {
            for (ApsWeekDayWrap apsWeekDayWrap : task.getAll()) {
                apsWeekDayWrap.setApsWorkOrderId(task.getApsWorkDetail().getApsWorkOrderId());
                apsWeekDayWrap.setApsWorkDetailId(task.getApsWorkDetail().getId());
            }
        }
        List<ApsDetailWeekday> apsDetailWeekdayList = orderCache.getTaskList()
                .stream()
                .flatMap(e -> e.getAll().stream().map(machineCalendar -> {
                    ApsDetailWeekday calendar = new ApsDetailWeekday();

                    calendar.setOrderId(e.getApsWorkDetail().getOrderId());
                    calendar.setApsOrderId(e.getApsWorkDetail().getApsOrderId());
                    calendar.setApsBomId(e.getApsWorkDetail().getApsWorkOrderId());
                    calendar.setPhaseId(e.getApsWorkDetail().getPhaseId());
                    calendar.setPhaseCode(e.getApsWorkDetail().getPhaseCode());
                    calendar.setPhaseName(e.getApsWorkDetail().getPhaseName());
                    calendar.setApsWorkOrderId(e.getApsWorkDetail().getApsWorkOrderId());
                    calendar.setApsWorkDetailId(e.getApsWorkDetail().getId());
                    calendar.setGroupId(e.getApsWorkDetail().getGroupId());

                    calendar.setWorkTime(machineCalendar.getSecond());
                    calendar.setPlanStartDateTime(machineCalendar.getStart());
                    calendar.setPlanEndDateTime(machineCalendar.getEnd());
                    calendar.setShiftId(machineCalendar.getShiftId());
                    calendar.setShiftSeq(machineCalendar.getShiftSeq());
                    calendar.setShiftStartDateTime(machineCalendar.getShiftStartDateTime());
                    calendar.setShiftEndDateTime(machineCalendar.getShiftEndDateTime());
                    calendar.setShiftDay(machineCalendar.getShiftDay());
                    calendar.setStationGroupId(e.getApsWorkDetail().getStationGroupId());
                    calendar.setStationId(e.getApsWorkDetail().getStationId());
                    calendar.setState(2); // 3 已排产
                    calendar.setOrderType(orderCache.getOrderIdMap().get(e.getApsWorkOrder().getOrderId()).getOrderType());
                    return calendar;
                }))
                .collect(Collectors.toList());
        apsDetailWeekdayMapper.insertBatch(apsDetailWeekdayList, 2000);

        Map<Integer, List<ApsWorkOrderTemp>> collect1 = orderCache.getApsWorkOrderTempList().stream().collect(Collectors.groupingBy(e -> e.getApsWorkOrder().getApsOrderId()));
        // 更新 apsOrder
        for (ApsOrder apsOrder : orderCache.getApsOrderList()) {
            List<ApsWorkOrderTemp> apsWorkOrderTemps = collect1.get(apsOrder.getId());
            ApsWorkOrderTemp min = apsWorkOrderTemps.stream().min(Comparator.comparing(e -> e.getApsWorkOrder().getPlanStartDateTime())).get();
            ApsWorkOrderTemp max = apsWorkOrderTemps.stream().max(Comparator.comparing(e -> e.getApsWorkOrder().getPlanEndDateTime())).get();
            apsOrder.setPlanStartDateTime(min.getApsWorkOrder().getPlanStartDateTime());
            apsOrder.setPlanEndDateTime(max.getApsWorkOrder().getPlanEndDateTime());
        }
        apsOrderMapper.updateBatchById(orderCache.getApsOrderList(), 500);

        // 更新 order
        for (Order order : orderCache.getOrderList()) {
            List<ApsOrder> apsOrders = orderCache.getApsOrderOrderIdMap().get(order.getId());
            ApsOrder min = apsOrders.stream().min(Comparator.comparing(ApsOrder::getPlanStartDateTime)).get();
            ApsOrder max = apsOrders.stream().max(Comparator.comparing(ApsOrder::getPlanEndDateTime)).get();
            order.setPlanStartDateTime(min.getPlanStartDateTime());
            order.setPlanEndDateTime(max.getPlanEndDateTime());
        }
        orderMapper.updateBatchById(orderCache.getOrderList(), 100);
    }


    public void initOrderAndApsOrder(PlanReSchedulingReqVo reqVo, PlanOrderCache orderCache) {
        // order
        List<Order> orderList = orderMapper.lambdaQuery()
                .in(Order::getOrderType, ApsConstant.ORDER_TYPE_MAP.get(orderCache.getSchedulingSource()))
                .ge(Order::getOrderStatus, 6)
                .eq(Order::getId, reqVo.getOrderId())
                .list();

        if (CollUtil.isEmpty(orderList)) {
            throw SyExceptionUtils.e("没有待排产的订单");
        }

        orderCache.setOrderList(orderList);
        orderCache.setOrderIdMap(orderList.stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity())));
        orderCache.setSchedulingRule(reqVo.getSchedulingRule());

        // apsOrder
        List<Integer> orderIds = orderList.stream().map(BaseEntity::getId).collect(Collectors.toList());
        List<ApsOrder> apsOrderList = apsOrderMapper.lambdaQuery()
                .in(ApsOrder::getOrderId, orderIds)
                .in(ApsOrder::getOrderType, ApsConstant.ORDER_TYPE_MAP.get(orderCache.getSchedulingSource()))
                .list();
        orderCache.setApsOrderList(apsOrderList);
        orderCache.setApsOrderIdMap(apsOrderList.stream().collect(Collectors.toMap(ApsOrder::getId, Function.identity())));
        Map<Integer, List<ApsOrder>> orderMap = orderCache.getApsOrderList().stream().sorted(Comparator.comparing(ApsOrder::getSeq)).collect(Collectors.groupingBy(ApsOrder::getOrderId, LinkedHashMap::new, Collectors.toList()));
        orderCache.setApsOrderOrderIdMap(orderMap);


        // reverseOrder
        List<ReverseOrder> reverseOrderList = reverseOrderMapper.lambdaQuery()
                .in(ReverseOrder::getOrderId, orderIds)
                .isNotNull(ReverseOrder::getParentId)
                .list();

        orderCache.setReverseOrderList(reverseOrderList);
        orderCache.setReverseOrderOrderIdMap(reverseOrderList.stream().collect(Collectors.groupingBy(ReverseOrder::getOrderId)));

    }


    public void initOrderCacheRe(PlanFactoryCache factoryCache, PlanOrderCache orderCache) {

        // 初始化排产批次信息
        LocalDateTime now = LocalDateTime.now();
        orderCache.getApsBatch().setBatchNo(now.format(DatePattern.NORM_DATETIME_FORMATTER));
        orderCache.getApsBatch().setSchedulingDateTime(now);
        orderCache.getApsBatch().setStatus(2);
        apsBatchMapper.updateById(orderCache.getApsBatch());

        for (Order order : orderCache.getOrderList()) {
            order.setPlanStartDateTime(null);
            order.setPlanEndDateTime(null);
            order.setBatchId(orderCache.getApsBatch().getId());
            orderMapper.updateById(order);
        }

        // 初始化所有订单 BOM 的 List 和 Map
        List<OrderBom> orderBomList = orderBomMapper.lambdaQuery().in(OrderBom::getOrderId, orderCache.getOrderIdMap().keySet()).list();
        orderCache.setOrderBomList(orderBomList);

        Map<Integer, List<OrderBom>> orderBomOrderIdMap = orderBomList.stream().collect(Collectors.groupingBy(OrderBom::getOrderId));
        orderCache.setOrderBomOrderIdMap(orderBomOrderIdMap);

        Map<Integer, OrderBom> orderBomIdMap = orderBomList.stream().collect(Collectors.toMap(OrderBom::getId, Function.identity()));
        orderCache.setOrderBomIdMap(orderBomIdMap);

        // 初始化所有订单 PHASE 的 List 和 Map
        List<OrderPhase> orderPhaseList = orderPhaseMapper.lambdaQuery().in(OrderPhase::getOrderId, orderCache.getOrderIdMap().keySet()).list();
        orderCache.setOrderPhaseList(orderPhaseList);

        Map<Integer, List<OrderPhase>> orderPhaseOrderIdMap = orderPhaseList.stream().collect(Collectors.groupingBy(OrderPhase::getOrderId));
        orderCache.setOrderPhaseOrderIdMap(orderPhaseOrderIdMap);

        Map<Integer, OrderPhase> orderPhaseIdMap = orderPhaseList.stream().collect(Collectors.toMap(OrderPhase::getId, Function.identity()));
        orderCache.setOrderPhaseIdMap(orderPhaseIdMap);

        // 初始化所有包的 ApsWorkOrder
        List<ApsWorkOrder> apsWorkOrderList = apsWorkOrderMapper.lambdaQuery().in(ApsWorkOrder::getOrderId, orderCache.getOrderIdMap().keySet()).orderByAsc(ApsWorkOrder::getLevel).list();

        // 构建 ApsWorkOrder 缓存对象
        List<ApsWorkOrderTemp> apsWorkOrderTempList = apsWorkOrderList.stream()
                .map(e -> {
                    ApsWorkOrderTemp apsWorkOrderTemp = new ApsWorkOrderTemp();
                    apsWorkOrderTemp.setApsWorkOrder(e);
                    apsWorkOrderTemp.setId(e.getId());
                    apsWorkOrderTemp.setPid(e.getPid());
                    return apsWorkOrderTemp;
                })
                .collect(Collectors.toList());

        orderCache.setApsWorkOrderTempList(apsWorkOrderTempList);
        orderCache.setApsWorkOrderTempIdMap(orderCache.getApsWorkOrderTempList().stream().collect(Collectors.toMap(ApsWorkOrderTemp::getId, Function.identity())));
        orderCache.setApsWorkOrderTempPidMap(orderCache.getApsWorkOrderTempList().stream().filter(e -> e.getPid() != null).collect(Collectors.groupingBy(ApsWorkOrderTemp::getPid, Collectors.toList())));

        // 取出倒排表数据
        List<ReverseDetail> reverseDetails = reverseDetailMapper.lambdaQuery().in(ReverseDetail::getOrderId, orderCache.getOrderIdMap().keySet()).list();
        Map<String, ReverseDetail> collect1 = reverseDetails.stream().collect(Collectors.toMap(e -> e.getOrderId() + ":" + e.getBomId() + ":" + e.getPhaseId(), Function.identity(), (e1, e2) -> e1.getReverseEndTime().isBefore(e2.getReverseEndTime()) ? e1 : e2));
        reverseDetails = collect1.values().stream().sorted(Comparator.comparing(ReverseDetail::getReverseEndTime).thenComparing(ReverseDetail::getPhaseId)).collect(Collectors.toList());
        Map<Integer, List<ReverseDetail>> reverseDetailIdMap = reverseDetails.stream().collect(Collectors.groupingBy(ReverseDetail::getOrderId));
        Map<Integer, Map<Integer, ReverseDetail>> collect = reverseDetails.stream().collect(Collectors.groupingBy(ReverseDetail::getOrderId, Collectors.toMap(ReverseDetail::getPhaseId, Function.identity())));

        orderCache.setReverseDetailList(reverseDetails);
        orderCache.setReverseDetailOrderIdMap(reverseDetailIdMap);
        orderCache.setReverseDetailOrderIdPhaseIdMap(collect);

        List<ApsWorkDetail> apsWorkDetailList = apsWorkDetailMapper.lambdaQuery()
                .in(ApsWorkDetail::getOrderId, orderCache.getOrderIdMap().keySet())
//                .lt(ApsWorkDetail::getFinishCount, "0.5")
                .eq(ApsWorkDetail::getType, 2)
                .list();

        List<ApsWorkDetailTemp> apsWorkDetailTempList = apsWorkDetailList.stream()
                .map(e -> {
                    ApsWorkDetailTemp temp = new ApsWorkDetailTemp();
                    temp.setApsWorkOrderTemp(orderCache.getApsWorkOrderTempIdMap().get(e.getApsWorkOrderId()));
                    temp.setApsWorkDetail(e);
                    temp.setApsWorkOrder(temp.getApsWorkOrderTemp().getApsWorkOrder());
                    return temp;
                })
                .collect(Collectors.toList());

        List<ApsTask> tasks = apsWorkDetailTempList.stream()
                .map(e -> {
                    ApsTask t = new ApsTask(e.getApsWorkDetail());
                    t.setApsWorkOrderTemp(e.getApsWorkOrderTemp());
                    t.setApsWorkDetailTemp(e);
                    t.setApsWorkOrder(e.getApsWorkOrder());
                    t.setId(e.getApsWorkDetail().getId());
                    t.setConsumerStatus(1);
                    t.setConsumerCount(new BigDecimal(0));
                    t.setStatus(2);
                    return t;
                })
//                .sorted(Comparator.comparing(t -> t.getApsWorkDetail().getDetailSeq()))
                .collect(Collectors.toList());
        orderCache.setTaskList(tasks);


        Map<Integer, Map<Integer, List<ApsTask>>> workOrderIdPhaseSeqMap = tasks.stream().collect(Collectors.groupingBy(e -> e.getApsWorkOrderTemp().getId(), Collectors.groupingBy(ApsTask::getPhaseSeq)));
        orderCache.setWorkOrderIdPhaseSeqMap(workOrderIdPhaseSeqMap);

        Map<Integer, List<ApsTask>> workOrderPidMap = tasks.stream().filter(e -> e.getApsWorkOrderPid() != null).collect(Collectors.groupingBy(ApsTask::getApsWorkOrderPid));
        orderCache.setWorkOrderPidMap(workOrderPidMap);

        for (Order order : orderCache.getOrderList()) {
            order.setPlanStartDateTime(null);
            order.setPlanEndDateTime(null);
        }

        for (ApsOrder apsOrder : orderCache.getApsOrderList()) {
            apsOrder.setPlanStartDateTime(null);
            apsOrder.setPlanEndDateTime(null);
        }

    }

    @Transactional
    public void schedulingMbd(OrderScheduleReqVo reqVo) {

        log.info("scheduling reqVo: {}", JsonUtils.toJsonString(reqVo));

        PlanOrderCache orderCache = new PlanOrderCache();
        PlanFactoryCache factoryCache = new PlanFactoryCache();

        orderCache.setSchedulingSource("mbd");
        PlanSchedulingReqVo reqVo1 = new PlanSchedulingReqVo();
        initOrderAndApsOrder(reqVo1, orderCache);

        valid(orderCache);

        lock(orderCache);

        clear(orderCache);

        initFactoryCache(factoryCache, orderCache);

        initOrderCache(factoryCache, orderCache);

        scheduleTask(factoryCache, orderCache);

        saveAndUpateMbd(orderCache);

       try {
            LantekPrePushReqVo pushPlanVo = new LantekPrePushReqVo();
            List<Integer> pushOrderIds = orderCache.getOrderList().stream().map(Order::getId).collect(Collectors.toList());
            pushPlanVo.setOrderIds(pushOrderIds);
            nestService.pushPrePlanAdd(pushPlanVo);
        } catch (Exception e) {
            log.info("零件计划同步失败。");
        }

    }

    private void saveAndUpateMbd(PlanOrderCache orderCache) {

        // 保存所有的workOrder
        List<ApsWorkOrderTemp> collect = orderCache.getApsWorkOrderTempList().stream().filter(e -> e.getPid() == null).collect(Collectors.toList());
        Map<Integer, ApsWorkOrderTemp> apsWorkOrderTempIdMap = orderCache.getApsWorkOrderTempIdMap();
        Map<Integer, List<ApsWorkOrderTemp>> apsWorkOrderTempPidMap = orderCache.getApsWorkOrderTempPidMap();

        List<ApsWorkOrderTemp> children = new ArrayList<>(collect);
        while (CollUtil.isNotEmpty(children)) {
            List<ApsWorkOrder> saveBatch = children.stream()
                    .map(e -> {
                        if (e.getPid() != null) {
                            e.getApsWorkOrder().setPid(apsWorkOrderTempIdMap.get(e.getPid()).getApsWorkOrder().getId());
                        }
                        e.getApsWorkOrder().setStatus(2);
                        return e.getApsWorkOrder();
                    })
                    .collect(Collectors.toList());
            apsWorkOrderMapper.insertBatch(saveBatch, 1000);

            List<ApsWorkOrderTemp> temp = new ArrayList<>();

            for (ApsWorkOrderTemp child : children) {
                List<ApsWorkOrderTemp> apsWorkOrderTemps = apsWorkOrderTempPidMap.get(child.getId());
                if (CollUtil.isNotEmpty(apsWorkOrderTemps)) {
                    temp.addAll(apsWorkOrderTemps);
                }
            }

            children = temp;
        }

        // 保存所有的workDetail
        for (ApsTask task : orderCache.getTaskList()) {
            ApsWorkOrderTemp apsWorkOrderTemp = apsWorkOrderTempIdMap.get(task.getApsWorkOrderId());
            task.getApsWorkDetailTemp().getApsWorkDetail().setApsWorkOrderId(apsWorkOrderTemp.getApsWorkOrder().getId());
            task.getApsWorkDetailTemp().getApsWorkDetail().setApsWorkOrderPid(apsWorkOrderTemp.getApsWorkOrder().getPid());
            task.getApsWorkDetail().setStatus(2);
        }
        List<ApsWorkDetail> apsWorkDetailList = orderCache.getTaskList().stream().map(ApsTask::getApsWorkDetail).collect(Collectors.toList());
        apsWorkDetailMapper.insertBatch(apsWorkDetailList, 2000);


        // 保存所有的apsDetailWeekday
        for (ApsTask task : orderCache.getTaskList()) {
            for (ApsWeekDayWrap apsWeekDayWrap : task.getAll()) {
                apsWeekDayWrap.setApsWorkOrderId(task.getApsWorkDetail().getApsWorkOrderId());
                apsWeekDayWrap.setApsWorkDetailId(task.getApsWorkDetail().getId());
            }
        }
        List<ApsDetailWeekday> apsDetailWeekdayList = orderCache.getTaskList()
                .stream()
                .flatMap(e -> e.getAll().stream().map(machineCalendar -> {
                    ApsDetailWeekday calendar = new ApsDetailWeekday();

                    calendar.setOrderId(e.getApsWorkDetail().getOrderId());
                    calendar.setApsOrderId(e.getApsWorkDetail().getApsOrderId());
                    calendar.setApsBomId(e.getApsWorkDetail().getApsWorkOrderId());
                    calendar.setPhaseId(e.getApsWorkDetail().getPhaseId());
                    calendar.setPhaseCode(e.getApsWorkDetail().getPhaseCode());
                    calendar.setPhaseName(e.getApsWorkDetail().getPhaseName());
                    calendar.setApsWorkOrderId(e.getApsWorkDetail().getApsWorkOrderId());
                    calendar.setApsWorkDetailId(e.getApsWorkDetail().getId());
                    calendar.setGroupId(e.getApsWorkDetail().getGroupId());

                    calendar.setWorkTime(machineCalendar.getSecond());
                    calendar.setPlanStartDateTime(machineCalendar.getStart());
                    calendar.setPlanEndDateTime(machineCalendar.getEnd());
                    calendar.setShiftId(machineCalendar.getShiftId());
                    calendar.setShiftSeq(machineCalendar.getShiftSeq());
                    calendar.setShiftStartDateTime(machineCalendar.getShiftStartDateTime());
                    calendar.setShiftEndDateTime(machineCalendar.getShiftEndDateTime());
                    calendar.setShiftDay(machineCalendar.getShiftDay());
                    calendar.setStationGroupId(e.getApsWorkDetail().getStationGroupId());
                    calendar.setStationId(e.getApsWorkDetail().getStationId());
                    calendar.setState(2); // 3 已排产
                    calendar.setOrderType(orderCache.getOrderIdMap().get(e.getApsWorkOrder().getOrderId()).getOrderType());
                    return calendar;
                }))
                .collect(Collectors.toList());
        apsDetailWeekdayMapper.insertBatch(apsDetailWeekdayList, 2000);

        Map<Integer, List<ApsWorkOrderTemp>> collect1 = orderCache.getApsWorkOrderTempList().stream().collect(Collectors.groupingBy(e -> e.getApsWorkOrder().getApsOrderId()));
        // 更新 apsOrder
        for (ApsOrder apsOrder : orderCache.getApsOrderList()) {
            List<ApsWorkOrderTemp> apsWorkOrderTemps = collect1.get(apsOrder.getId());
            ApsWorkOrderTemp min = apsWorkOrderTemps.stream().min(Comparator.comparing(e -> e.getApsWorkOrder().getPlanStartDateTime())).get();
            ApsWorkOrderTemp max = apsWorkOrderTemps.stream().max(Comparator.comparing(e -> e.getApsWorkOrder().getPlanEndDateTime())).get();
            apsOrder.setOrderStatus(5);
            apsOrder.setPlanStartDateTime(min.getApsWorkOrder().getPlanStartDateTime());
            apsOrder.setPlanEndDateTime(max.getApsWorkOrder().getPlanEndDateTime());
            apsOrder.setOrderStatus(6);
            apsOrder.setLocked(2);
        }
        apsOrderMapper.updateBatchById(orderCache.getApsOrderList(), 500);

        // 更新 order
        for (Order order : orderCache.getOrderList()) {
            List<ApsOrder> apsOrders = orderCache.getApsOrderOrderIdMap().get(order.getId());
            ApsOrder min = apsOrders.stream().min(Comparator.comparing(ApsOrder::getPlanStartDateTime)).get();
            ApsOrder max = apsOrders.stream().min(Comparator.comparing(ApsOrder::getPlanEndDateTime)).get();
            order.setPlanStartDateTime(min.getPlanStartDateTime());
            order.setPlanEndDateTime(max.getPlanEndDateTime());
            order.setOrderStatus(6);
            order.setPushStatus(1);
            order.setLocked(2);
        }
        orderMapper.updateBatchById(orderCache.getOrderList(), 100);

        orderCache.getApsBatch().setStatus(2);
        apsBatchMapper.updateById(orderCache.getApsBatch());
    }

    @Transactional
    public void unlockMbd(OrderScheduleReqVo reqVo) {
        stringRedisTemplate.delete(ApsConstant.APS_SCHEDULING_LOCK);
    }

    @Transactional(readOnly = true)
    public List<PlanMonthlyResVo> monthly(PlanMonthlyReqVo reqVo) {
        if (reqVo.getMonth() == null) {
            throw SyExceptionUtils.e("月份不能为空");
        }

        LocalDate current = LocalDate.parse(reqVo.getMonth() + "-01", DatePattern.NORM_DATE_FORMATTER);
        LocalDate startDate = current.withDayOfMonth(1);

        LocalDate endDate = startDate.with(TemporalAdjusters.lastDayOfMonth());

        Map<Integer, List<Integer>> map = new HashMap<>();
        map.put(1, Lists.newArrayList(6, 7, 8));
        map.put(2, Lists.newArrayList(9, 10));

        long until = startDate.until(endDate, ChronoUnit.DAYS);
        List<LocalDate> allDate = Stream.iterate(startDate, s -> s.plusDays(1)).limit(until + 1).collect(Collectors.toList());

        // 下料统计
        QueryWrapper<ApsWorkDetail> cutQw = Wrappers.query();
        cutQw.like(" awo.assembly_path ", "P1");
        cutQw.in(" awd.phase_code ", "JG", "HG");
        cutQw.ge(" DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ", startDate + " 00:00:00");
        cutQw.le(" DATE_FORMAT(DATE_SUB(awd.plan_end_date_time, INTERVAL 7 HOUR), '%Y-%m-%d')  ", endDate + " 23:59:59");
        cutQw.in(" awd.order_type", 1);
        cutQw.in(" awd.type", 2);
        cutQw.like(StringUtils.hasText(reqVo.getConstructNo()), " o.construct_no ", reqVo.getConstructNo());
        cutQw.like(StringUtils.hasText(reqVo.getMaterName()), " o.mater_name ", reqVo.getMaterName());
        cutQw.in(reqVo.getOrderStatus() != null, " o.order_status ", map.get(reqVo.getOrderStatus()));
        cutQw.groupBy(" awd.order_id ");
        List<Integer> cutOrderIdList = apsWorkDetailMapper.orderIdList2(cutQw);

        // 配套统计
        QueryWrapper<ApsWorkDetail> completeQw = Wrappers.query();
        cutQw.like(" awo.assembly_path ", "P1");
        completeQw.in(" awd.phase_code ", "KJ");
        completeQw.ge(" DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ", startDate + " 00:00:00");
        completeQw.le(" DATE_FORMAT(DATE_SUB(awd.plan_end_date_time, INTERVAL 7 HOUR), '%Y-%m-%d')  ", endDate + " 23:59:59");
        completeQw.in(" awd.order_type", 1);
        completeQw.in(" awd.type", 2);
        cutQw.like(StringUtils.hasText(reqVo.getConstructNo()), " o.construct_no ", reqVo.getConstructNo());
        cutQw.like(StringUtils.hasText(reqVo.getMaterName()), " o.mater_name ", reqVo.getMaterName());
        cutQw.in(reqVo.getOrderStatus() != null, " o.order_status ", map.get(reqVo.getOrderStatus()));
        cutQw.groupBy(" awd.order_id ");
        List<Integer> completeOrderIdList = apsWorkDetailMapper.orderIdList2(completeQw);

        // 拼焊统计
        QueryWrapper<ApsWorkDetail> weldingQw = countSinglePhase("PH", reqVo, startDate, endDate, cutQw);
        List<Integer> weldingOrderIdList = apsWorkDetailMapper.orderIdList(weldingQw);

        // 喷粉统计
        QueryWrapper<ApsWorkDetail> sprayingQw = countSinglePhase("PT", reqVo, startDate, endDate, cutQw);
        List<Integer> sprayingOrderIdList = apsWorkDetailMapper.orderIdList(sprayingQw);

        // 取订单并集
        HashSet<Integer> allOrderId = new HashSet<>();
        allOrderId.addAll(cutOrderIdList);
        allOrderId.addAll(completeOrderIdList);
        allOrderId.addAll(weldingOrderIdList);
        allOrderId.addAll(sprayingOrderIdList);
        if (CollUtil.isNotEmpty(reqVo.getMbdIds())) {
            allOrderId.addAll(reqVo.getMbdIds());
        }

//allOrderId.clear();
//allOrderId.add(453);
        Map<String, Integer> partTypes = new HashMap<>();
        partTypes.put("顶梁", 1);
        partTypes.put("掩护梁", 2);
        partTypes.put("底座", 3);
        partTypes.put("侧板", 4);
        partTypes.put("连杆", 5);

        Map<Integer, String> partTypesReverse = new HashMap<>();
        for (Map.Entry<String, Integer> entry : partTypes.entrySet()) {
            partTypesReverse.put(entry.getValue(), entry.getKey()); // 互换键和值
        }


        Map<String, Integer> orderTypes = new HashMap<>();
        orderTypes.put("D", 1);
        orderTypes.put("Y", 2);
        orderTypes.put("Z", 3);
        orderTypes.put("C", 4);
        orderTypes.put("L", 5);
        orderTypes.put("T", 6);
        orderTypes.put("B", 7);

        List<Order> orders = orderMapper.selectBatchIds(allOrderId);
        orders = orders.stream()
                .filter(e -> {

                    if (e.getOrderType() == 2) {
                        return true;
                    }

                    if (StringUtils.hasText(reqVo.getConstructNo())) {
                        return e.getConstructNo().contains(reqVo.getConstructNo());
                    }

                    if (StringUtils.hasText(reqVo.getMaterType())) {
                        String[] str = reqVo.getMaterType().split(",");
                        for (String s : str) {
                            if (e.getMaterName().contains(partTypesReverse.get(Integer.parseInt(s)))) {
                                return true;
                            }
                        }
                        return false;
                    }

                    return true;
                })
                .filter(e -> e.getMaterName().contains("顶梁") || e.getMaterName().contains("底座") || e.getMaterName().contains("掩护梁") || e.getMaterName().contains("侧板") || e.getMaterName().contains("连杆"))
                .collect(Collectors.toList());

        orders.sort(Comparator.comparing((Order o)-> -o.getOrderType()).thenComparing(Order::getConstructNo).thenComparing(e -> {
            String suffix = e.getSuffix();
            if(suffix == null){
                return e.getPlmId();
            }
            return orderTypes.get(e.getSuffix()).toString();
        }));
        if (CollUtil.isEmpty(orders)) {
            return new ArrayList<>();
        }

        // 赋值 项目和部件
        List<PlanMonthlyResVo> resVoList = new ArrayList<>();

        // 下料工序，总计划，已完成，剩余
        QueryWrapper<ApsWorkDetail> cutWrapper = Wrappers.query();
        cutWrapper.in("  awd.phase_code ", "JG", "HG");
        cutWrapper.eq("  awd.type ", 2);
        cutWrapper.like(" awo.assembly_path ", "P1");
        cutWrapper.in(" awd.order_id ", allOrderId);
        cutWrapper.groupBy(" awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d')  ");

        List<PlanMonthlyProcessDateResVo> cutList = apsWorkDetailMapper.cutList(cutWrapper);
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdDateCutMap = cutList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));


        QueryWrapper<ApsWorkDetail> cutOtherWrapper = Wrappers.query();
        cutOtherWrapper.in("  awd.phase_code ", "JG", "HG");
        cutOtherWrapper.eq("  awd.type ", 2);
        cutOtherWrapper.and(w -> {
            w.like(" o.mater_name ", "侧板");
            w.or();
            w.like(" o.mater_name ", "连杆");
        });
        cutOtherWrapper.in(" awd.order_id ", allOrderId);
        cutOtherWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");

        List<PlanMonthlyProcessDateResVo> cutOtherList = apsWorkDetailMapper.cebanCutList(cutOtherWrapper);
        for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : cutOtherList) {
            planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdDateCutOtherMap = cutOtherList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));


        QueryWrapper<ApsWorkDetail> completeWrapper = Wrappers.query();
        completeWrapper.in("  awd.phase_code ", "KJ");
        completeWrapper.eq("  awd.type ", 2);
        completeWrapper.like("  awo2.plm_id ", "P1");
        completeWrapper.in(" awd.order_id ", allOrderId);
        completeWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(awd.plan_start_date_time, '%Y-%m-%d') ");
        List<PlanMonthlyProcessDateResVo> completeList = apsWorkDetailMapper.kjList(completeWrapper);
        for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : completeList) {
            planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdDateCompleteMap = completeList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));

        QueryWrapper<ApsWorkDetail> weldingWrapper = Wrappers.query();
        weldingWrapper.in("  awd.phase_code ", "WH");
        weldingWrapper.eq("  awd.type ", 2);
        weldingWrapper.in(" awd.order_id ", allOrderId);
        weldingWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(awd.plan_start_date_time, '%Y-%m-%d') ");
        List<PlanMonthlyProcessDateResVo> weldingList = apsWorkDetailMapper.whList(weldingWrapper);
        for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : weldingList) {
            planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdDateWeldingMap = weldingList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));


        QueryWrapper<ApsWorkDetail> sprayingWrapper = Wrappers.query();
        sprayingWrapper.in("  awd.phase_code ", "PT");
        sprayingWrapper.eq("  awd.type ", 2);
        sprayingWrapper.in(" awd.order_id ", allOrderId);
        sprayingWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(awd.plan_start_date_time, '%Y-%m-%d') ");
        List<PlanMonthlyProcessDateResVo> sprayingList = apsWorkDetailMapper.whList(sprayingWrapper);
        for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : sprayingList) {
            planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdDateSprayingMap = sprayingList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));

        List<String> dajian = Arrays.asList("D", "Z", "Y");
        List<String> xiaojian = Arrays.asList("C", "L", "T", "B");
        List<Integer> dajianOrderId = orders.stream().filter(e -> e.getOrderType().equals(1) && e.getSuffix() != null && dajian.contains(e.getSuffix())).map(BaseEntity::getId).collect(Collectors.toList());
        List<Integer> xiaojianOrderId = orders.stream().filter(e -> e.getOrderType().equals(1) && e.getSuffix() != null && xiaojian.contains(e.getSuffix())).map(BaseEntity::getId).collect(Collectors.toList());


        List<PlanMonthlyProcessDateResVo> completeCbList = new ArrayList<>();
        if(CollUtil.isNotEmpty(xiaojianOrderId)){
            QueryWrapper<ApsWorkDetail> completeCbWrapper = Wrappers.query();
            completeCbWrapper.in("  awd.phase_code ", "KJ");
            completeCbWrapper.eq("  awd.type ", 2);
            completeCbWrapper.like("  awo.level ", 3);
            completeCbWrapper.in(" awd.order_id ", xiaojianOrderId);
            completeCbWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(awd.plan_start_date_time, '%Y-%m-%d') ");
            completeCbList = apsWorkDetailMapper.kjList(completeCbWrapper);
            for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : completeCbList) {
                planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
            }
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdCbDateCompleteMap = completeCbList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));


        List<OrderBom> dajianBom = new ArrayList<>();
        if (CollUtil.isNotEmpty(dajianOrderId)) {
            dajianBom = orderBomMapper.lambdaQuery().eq(OrderBom::getLevel, 1).in(OrderBom::getOrderId, dajianOrderId).list();
        }
        List<OrderBom> xiaojianBom = new ArrayList<>();
        if (CollUtil.isNotEmpty(xiaojianOrderId)) {
            xiaojianBom = orderBomMapper.lambdaQuery().in(OrderBom::getOrderId, xiaojianOrderId).list();
        }

        Map<Integer, BigDecimal> materWeightMap = new HashMap<>();
        for (OrderBom orderBom : dajianBom) {
            materWeightMap.put(orderBom.getOrderId(), orderBom.getMaterWeight());
        }

//        .eq(OrderBom::getLevel, 2)

        List<OrderBom> level2Bom = xiaojianBom.stream().filter(e -> e.getLevel() == 2).collect(Collectors.toList());
        for (OrderBom orderBom : level2Bom) {
            materWeightMap.compute(orderBom.getOrderId(), (k, v) -> v == null ? orderBom.getMaterWeight() : v.add(orderBom.getMaterWeight()));
        }
        Map<Integer, List<OrderBom>> orderIdMap = xiaojianBom.stream().collect(Collectors.groupingBy(OrderBom::getOrderId));

        for (Order order : orders) {
            PlanMonthlyResVo resVo = new PlanMonthlyResVo();
            resVo.setOrderId(order.getId());
            resVo.setConstructNo(order.getConstructNo());
            resVo.setMaterName(order.getMaterName().replaceAll("[一|二|三|四|五|六]拼", ""));
            resVo.setOrderType(order.getOrderType());
            if(order.getFinishQty() == null){
                order.setFinishQty(0);
            }

            if (order.getMaterName().equals("侧板") || order.getMaterName().equals("连杆")) {

                List<OrderBom> jgOrderBomList = orderIdMap.getOrDefault(order.getId(), new ArrayList<>()).stream().filter(e -> e.getWorkChain().contains("JG") || e.getWorkChain().contains("HG")).collect(Collectors.toList());

                PlanMonthlyProcessResVo cutProcessResVo = new PlanMonthlyProcessResVo();
                cutProcessResVo.setProcess("下料");
                resVo.getProcessInfoList().add(cutProcessResVo);
                List<PlanMonthlyProcessCalendarResVo> cutDateQuantityList = getCeBanPinHan(allDate, startDate, endDate, orderIdDateCutOtherMap.getOrDefault(order.getId(), new ArrayList<>()), cutProcessResVo, order, jgOrderBomList);
                cutProcessResVo.getDateQuantityList().addAll(cutDateQuantityList);
                cutProcessResVo.setPlanCount(new BigDecimal(cutDateQuantityList.stream().map(PlanMonthlyProcessCalendarResVo::getQuantity).reduce(0, Integer::sum)));
                cutProcessResVo.setFinishCount(new BigDecimal(0));
                cutProcessResVo.setTotalCount(new BigDecimal(order.getOrderQty()));
                cutProcessResVo.setTodoCount(cutProcessResVo.getPlanCount().subtract(cutProcessResVo.getFinishCount()));
                if (cutProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    cutProcessResVo.setPlanFinishRate(cutProcessResVo.getFinishCount().divide(cutProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

                List<OrderBom> kjOrderBomList = orderIdMap.getOrDefault(order.getId(), new ArrayList<>()).stream().filter(e -> e.getLevel() == 3 && !e.getMaterType().equals("Component")).collect(Collectors.toList());
                PlanMonthlyProcessResVo completeProcessResVo = new PlanMonthlyProcessResVo();
                completeProcessResVo.setProcess("配套");
                resVo.getProcessInfoList().add(completeProcessResVo);
                List<PlanMonthlyProcessCalendarResVo> completeDateQuantityList = getCeBanPinHan(allDate, startDate, endDate, orderIdCbDateCompleteMap.getOrDefault(order.getId(), new ArrayList<>()), completeProcessResVo, order, kjOrderBomList);
                completeProcessResVo.getDateQuantityList().addAll(completeDateQuantityList);
                completeProcessResVo.setPlanCount(new BigDecimal(cutDateQuantityList.stream().map(PlanMonthlyProcessCalendarResVo::getQuantity).reduce(0, Integer::sum)));
                completeProcessResVo.setFinishCount(new BigDecimal(0));
                completeProcessResVo.setTotalCount(new BigDecimal(order.getOrderQty()));
                completeProcessResVo.setTodoCount(completeProcessResVo.getPlanCount().subtract(completeProcessResVo.getFinishCount()));
                if (completeProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    completeProcessResVo.setPlanFinishRate(completeProcessResVo.getFinishCount().divide(completeProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

                PlanMonthlyProcessResVo weldingProcessResVo = new PlanMonthlyProcessResVo();
                weldingProcessResVo.setProcess("拼焊");
                resVo.getProcessInfoList().add(weldingProcessResVo);

                List<OrderBom> level2CebanBom = orderIdMap.getOrDefault(order.getId(), new ArrayList<>()).stream().filter(e -> e.getLevel() == 2).collect(Collectors.toList());
                List<PlanMonthlyProcessCalendarResVo> weldingDateQuantityList = getCeBanPinHan(allDate, startDate, endDate, orderIdDateWeldingMap.getOrDefault(order.getId(), new ArrayList<>()), weldingProcessResVo, order, level2CebanBom);
                weldingProcessResVo.getDateQuantityList().addAll(weldingDateQuantityList);
                weldingProcessResVo.setPlanCount(new BigDecimal(weldingDateQuantityList.stream().map(PlanMonthlyProcessCalendarResVo::getQuantity).reduce(0, Integer::sum)));
                weldingProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                weldingProcessResVo.setTotalCount(new BigDecimal(order.getOrderQty()));
                weldingProcessResVo.setTodoCount(weldingProcessResVo.getTotalCount().subtract(weldingProcessResVo.getFinishCount()));
                if (weldingProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    weldingProcessResVo.setPlanFinishRate(weldingProcessResVo.getFinishCount().divide(weldingProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

                PlanMonthlyProcessResVo sprayingProcessResVo = new PlanMonthlyProcessResVo();
                sprayingProcessResVo.setProcess("喷粉");
                resVo.getProcessInfoList().add(sprayingProcessResVo);
                List<PlanMonthlyProcessCalendarResVo> sprayingDateQuantityList = getCeBanPinHan(allDate, startDate, endDate, orderIdDateSprayingMap.getOrDefault(order.getId(), new ArrayList<>()), sprayingProcessResVo, order, level2CebanBom);
                sprayingProcessResVo.getDateQuantityList().addAll(sprayingDateQuantityList);
                sprayingProcessResVo.setPlanCount(new BigDecimal(sprayingDateQuantityList.stream().map(PlanMonthlyProcessCalendarResVo::getQuantity).reduce(0, Integer::sum)));
                sprayingProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                sprayingProcessResVo.setTotalCount(new BigDecimal(order.getOrderQty()));
                sprayingProcessResVo.setTodoCount(sprayingProcessResVo.getTotalCount().subtract(sprayingProcessResVo.getFinishCount()));
                if (sprayingProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    sprayingProcessResVo.setPlanFinishRate(sprayingProcessResVo.getFinishCount().divide(sprayingProcessResVo.getTotalCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }


            } else {
                PlanMonthlyProcessResVo cutProcessResVo = new PlanMonthlyProcessResVo();
                cutProcessResVo.setProcess("下料");
                resVo.getProcessInfoList().add(cutProcessResVo);
                List<PlanMonthlyProcessCalendarResVo> cutDateQuantityList = genPzChildData(allDate, startDate, endDate, orderIdDateCutMap.get(order.getId()), cutProcessResVo, order);
                cutProcessResVo.getDateQuantityList().addAll(cutDateQuantityList);
                cutProcessResVo.setPlanCount(new BigDecimal(cutDateQuantityList.stream().map(PlanMonthlyProcessCalendarResVo::getQuantity).reduce(0, Integer::sum)));
                cutProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                cutProcessResVo.setTodoCount(cutProcessResVo.getTotalCount().subtract(cutProcessResVo.getFinishCount()));
                if (cutProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    cutProcessResVo.setPlanFinishRate(cutProcessResVo.getFinishCount().divide(cutProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

                PlanMonthlyProcessResVo completeProcessResVo = new PlanMonthlyProcessResVo();
                completeProcessResVo.setProcess("配套");
                resVo.getProcessInfoList().add(completeProcessResVo);
                List<PlanMonthlyProcessCalendarResVo> completeDateQuantityList = genPzChildData(allDate, startDate, endDate, orderIdDateCompleteMap.get(order.getId()), completeProcessResVo, order);
                completeProcessResVo.getDateQuantityList().addAll(completeDateQuantityList);
                completeProcessResVo.setPlanCount(new BigDecimal(completeDateQuantityList.stream().map(PlanMonthlyProcessCalendarResVo::getQuantity).reduce(0, Integer::sum)));
                completeProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                completeProcessResVo.setTodoCount(completeProcessResVo.getTotalCount().subtract(completeProcessResVo.getFinishCount()));
                if (completeProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    completeProcessResVo.setPlanFinishRate(completeProcessResVo.getFinishCount().divide(completeProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }
                PlanMonthlyProcessResVo weldingProcessResVo = new PlanMonthlyProcessResVo();
                weldingProcessResVo.setProcess("拼焊");
                resVo.getProcessInfoList().add(weldingProcessResVo);
                List<PlanMonthlyProcessCalendarResVo> weldingDateQuantityList = getSingle(allDate, startDate, endDate, orderIdDateWeldingMap.get(order.getId()), weldingProcessResVo, order);
                weldingProcessResVo.getDateQuantityList().addAll(weldingDateQuantityList);
                weldingProcessResVo.setPlanCount(new BigDecimal(weldingDateQuantityList.stream().map(PlanMonthlyProcessCalendarResVo::getQuantity).reduce(0, Integer::sum)));
                weldingProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                weldingProcessResVo.setTodoCount(weldingProcessResVo.getTotalCount().subtract(weldingProcessResVo.getFinishCount()));
                if (weldingProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    weldingProcessResVo.setPlanFinishRate(weldingProcessResVo.getFinishCount().divide(weldingProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

                PlanMonthlyProcessResVo sprayingProcessResVo = new PlanMonthlyProcessResVo();
                sprayingProcessResVo.setProcess("喷粉");
                resVo.getProcessInfoList().add(sprayingProcessResVo);
                List<PlanMonthlyProcessCalendarResVo> sprayingDateQuantityList = getSingle(allDate, startDate, endDate, orderIdDateSprayingMap.get(order.getId()), sprayingProcessResVo, order);
                sprayingProcessResVo.getDateQuantityList().addAll(sprayingDateQuantityList);
                sprayingProcessResVo.setPlanCount(new BigDecimal(sprayingDateQuantityList.stream().map(PlanMonthlyProcessCalendarResVo::getQuantity).reduce(0, Integer::sum)));
                sprayingProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                sprayingProcessResVo.setTodoCount(sprayingProcessResVo.getTotalCount().subtract(sprayingProcessResVo.getFinishCount()));
                if (sprayingProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    sprayingProcessResVo.setPlanFinishRate(sprayingProcessResVo.getFinishCount().divide(sprayingProcessResVo.getTotalCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

            }


            if (order.getOrderType().equals(1)) {
                BigDecimal bigDecimal = materWeightMap.get(order.getId());
                if (bigDecimal != null) {
                    for (PlanMonthlyProcessResVo planMonthlyProcessResVo : resVo.getProcessInfoList()) {
                        planMonthlyProcessResVo.setUnitWeight(bigDecimal);
                        planMonthlyProcessResVo.setPlanWeight(bigDecimal.multiply(new BigDecimal(order.getOrderQty())));
                    }
                }
            }

            if (resVo.getOrderType().equals(2) || resVo.getProcessInfoList().stream().filter(e-> e.getPlanCount().intValue() > 0).count() > 1) {
                resVoList.add(resVo);
            }

        }

        return resVoList;
    }

    public List<PlanMonthlyProcessCalendarResVo> getCeBanPinHan(List<LocalDate> allDate, LocalDate startDate, LocalDate endDate, List<PlanMonthlyProcessDateResVo> planMonthlyProcessDateResVos, PlanMonthlyProcessResVo weldingProcessResVo, Order order, List<OrderBom> orderBoms) {

        // 总数量
        int yestdayCount = 0;
        List<PlanMonthlyProcessCalendarResVo> collect = new ArrayList<>();
        for (LocalDate date : allDate) {
            PlanMonthlyProcessCalendarResVo calendarResVo = new PlanMonthlyProcessCalendarResVo();
            calendarResVo.setDate(date);
            int dayCount = calcMatchCount(planMonthlyProcessDateResVos.stream().filter(e1 -> e1 != null && date != null && e1.getDate() != null && !date.isBefore(e1.getDate())).collect(Collectors.toList()), orderBoms);
            calendarResVo.setQuantity(dayCount - yestdayCount);
            calendarResVo.setFinishCount(dayCount);
            yestdayCount = dayCount;
            collect.add(calendarResVo);
        }

        return collect;
    }

    public Integer calcMatchCount(List<PlanMonthlyProcessDateResVo> planMonthlyProcessDateResVos, List<OrderBom> orderBoms) {
        Map<Integer, List<PlanMonthlyProcessDateResVo>> bomIdMap = planMonthlyProcessDateResVos.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getBomId));

        Optional<BigDecimal> min = orderBoms.stream()
                .map(e -> {
                    List<PlanMonthlyProcessDateResVo> planProDetailDayResVos = bomIdMap.get(e.getId());
                    if (CollUtil.isEmpty(planProDetailDayResVos)) {
                        return new BigDecimal(0);
                    }
                    return planProDetailDayResVos.stream()
                            .filter(e1 -> e1.getTotalCount() != null)
                            .map(PlanMonthlyProcessDateResVo::getTotalCount)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
                            .divide(e.getTotalCount(), 0, RoundingMode.DOWN);
                })
                .min(BigDecimal::compareTo);
        return min.map(BigDecimal::intValue).orElse(0);
    }

    private QueryWrapper<ApsWorkDetail> countSinglePhase(String phaseCode, PlanMonthlyReqVo reqVo, LocalDate startDate, LocalDate endDate, QueryWrapper<ApsWorkDetail> cutQw) {
        QueryWrapper<ApsWorkDetail> sprayingQw = Wrappers.query();
        sprayingQw.in(" awd.phase_code ", phaseCode);
        sprayingQw.ge(" DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ", startDate + " 00:00:00");
        sprayingQw.le(" DATE_FORMAT(DATE_SUB(awd.plan_end_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ", endDate + " 23:59:59");
        sprayingQw.in(" awd.order_type", 1);
        sprayingQw.in(" awd.type", 2);
        cutQw.like(StringUtils.hasText(reqVo.getConstructNo()), " o.construct_no ", reqVo.getConstructNo());
        cutQw.like(StringUtils.hasText(reqVo.getMaterName()), " o.mater_name ", reqVo.getMaterName());
        if (reqVo.getOrderStatus() != null) {
            if (reqVo.getOrderStatus().equals(1)) {
                cutQw.in(" o.order_status ", 6, 7, 8);
            } else if (reqVo.getOrderStatus().equals(2)) {
                cutQw.in(" o.order_status ", 9, 10);
            } else {
                throw SyExceptionUtils.e("未知的订单状态");
            }
        } else {
            cutQw.ge(" o.order_status ", 6);
        }

        cutQw.like(StringUtils.hasText(reqVo.getConstructNo()), " o.construct_no ", reqVo.getConstructNo());


        cutQw.groupBy(" awd.order_id ");
        return sprayingQw;
    }

    public List<PlanMonthlyProcessCalendarResVo> getSingle(List<LocalDate> allDate, LocalDate startDate, LocalDate endDate, List<PlanMonthlyProcessDateResVo> planMonthlyProcessDateResVos, PlanMonthlyProcessResVo cutProcessResVo, Order order) {

        if (CollUtil.isEmpty(planMonthlyProcessDateResVos)) {
            cutProcessResVo.setTotalCount(BigDecimal.ZERO);
            cutProcessResVo.setFinishCount(BigDecimal.ZERO);
            return new ArrayList<>();
        }

        // 总数量
        PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo = planMonthlyProcessDateResVos.stream().min(Comparator.comparing(PlanMonthlyProcessDateResVo::getParentPartCount)).get();
        BigDecimal root = planMonthlyProcessDateResVo.getParentPartCount().multiply(new BigDecimal(order.getOrderQty()));
        cutProcessResVo.setTotalCount(root);

        // 完成数量
        BigDecimal finishCount = planMonthlyProcessDateResVos.stream().filter(e -> LocalDate.parse(e.getDateStr(), DatePattern.NORM_DATE_FORMATTER).isBefore(startDate)).map(PlanMonthlyProcessDateResVo::getTotalCount).reduce(BigDecimal.ZERO, BigDecimal::add);
        cutProcessResVo.setFinishCount(finishCount);

        // 之后每一天的
        List<PlanMonthlyProcessCalendarResVo> collect = allDate.stream()
                .map(e -> {
                    PlanMonthlyProcessCalendarResVo calendarResVo = new PlanMonthlyProcessCalendarResVo();
                    calendarResVo.setDate(e);
                    return calendarResVo;
                })
                .sorted(Comparator.comparing(PlanMonthlyProcessCalendarResVo::getDate))
                .collect(Collectors.toList());

        int sum = 0;
        for (PlanMonthlyProcessCalendarResVo currentDay : collect) {
            BigDecimal reduce = planMonthlyProcessDateResVos.stream().filter(e1 -> LocalDate.parse(e1.getDateStr(), DatePattern.NORM_DATE_FORMATTER).equals(currentDay.getDate())).map(PlanMonthlyProcessDateResVo::getTotalCount).reduce(BigDecimal.ZERO, BigDecimal::add);
            currentDay.setQuantity(reduce.intValue());
            sum += reduce.intValue();
            currentDay.setFinishCount(sum);
        }

        return collect;
    }

    public List<PlanMonthlyProcessCalendarResVo> genOtherChildData(List<LocalDate> allDate, LocalDate startDate, LocalDate endDate, List<PlanMonthlyProcessDateResVo> planMonthlyProcessDateResVos, PlanMonthlyProcessResVo cutProcessResVo, Order order) {

        // 总数量
        PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo = planMonthlyProcessDateResVos.stream().min(Comparator.comparing(PlanMonthlyProcessDateResVo::getParentPartCount)).get();
        BigDecimal root = planMonthlyProcessDateResVo.getParentPartCount().multiply(new BigDecimal(order.getOrderQty()));
        cutProcessResVo.setTotalCount(root);

        // 比例
        Map<String, BigDecimal> biliMap = planMonthlyProcessDateResVos.stream().collect(Collectors.toMap(PlanMonthlyProcessDateResVo::getPlmId, PlanMonthlyProcessDateResVo::getPartCount, (e1, e2) -> e1));

        // 完成数量
        Map<String, BigDecimal> finishCount = planMonthlyProcessDateResVos.stream().filter(e -> LocalDate.parse(e.getDateStr(), DatePattern.NORM_DATE_FORMATTER).isBefore(startDate)).collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getPlmId, Collectors.reducing(BigDecimal.ZERO, PlanMonthlyProcessDateResVo::getFinishCount, BigDecimal::add)));
        BigDecimal bigDecimal1 = biliMap.entrySet()
                .stream()
                .map(e -> {
                    BigDecimal bigDecimal = finishCount.get(e.getKey());
                    if (bigDecimal == null) {
                        return new BigDecimal(0);
                    }
                    if (bigDecimal.intValue() == 0) {
                        return new BigDecimal(0);
                    }
                    return bigDecimal.divide(e.getValue(), 2, RoundingMode.DOWN);
                })
                .min(BigDecimal::compareTo)
                .orElseGet(() -> new BigDecimal(0));

        cutProcessResVo.setFinishCount(bigDecimal1);

        // 之后每一天的
        List<PlanMonthlyProcessCalendarResVo> collect = allDate.stream()
                .map(e -> {
                    PlanMonthlyProcessCalendarResVo calendarResVo = new PlanMonthlyProcessCalendarResVo();
                    calendarResVo.setDate(e);
                    return calendarResVo;
                })
                .sorted(Comparator.comparing(PlanMonthlyProcessCalendarResVo::getDate))
                .collect(Collectors.toList());


        Map<LocalDate, Integer> map = new HashMap<>();
        int sum = 0;
        for (PlanMonthlyProcessCalendarResVo currentDay : collect) {
            Map<String, BigDecimal> dayPlmIdCount = planMonthlyProcessDateResVos.stream().filter(e1 -> LocalDate.parse(e1.getDateStr(), DatePattern.NORM_DATE_FORMATTER).equals(currentDay.getDate())).collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getPlmId, Collectors.reducing(BigDecimal.ZERO, PlanMonthlyProcessDateResVo::getTotalCount, BigDecimal::add)));

            BigDecimal jiashu = biliMap.entrySet()
                    .stream()
                    .map(e -> {
                        BigDecimal bigDecimal = finishCount.get(e.getKey());
                        if (bigDecimal == null) {
                            bigDecimal = new BigDecimal(0);
                        }

                        BigDecimal dayCount = dayPlmIdCount.getOrDefault(e.getKey(), BigDecimal.ZERO);
                        BigDecimal newFinish = bigDecimal.add(dayCount);
                        finishCount.put(e.getKey(), newFinish);
                        return newFinish.divide(e.getValue(), 0, RoundingMode.DOWN);
                    })
                    .min(BigDecimal::compareTo)
                    .orElseGet(() -> new BigDecimal(0));
            map.put(currentDay.getDate(), jiashu.intValue());

            Integer orDefault = map.getOrDefault(currentDay.getDate().minusDays(1), bigDecimal1.intValue());

            currentDay.setQuantity(jiashu.intValue() - orDefault);
            sum += currentDay.getQuantity();
            currentDay.setFinishCount(sum);
        }


        return collect;
    }

    public List<PlanMonthlyProcessCalendarResVo> genPzChildData(List<LocalDate> allDate, LocalDate startDate, LocalDate endDate, List<PlanMonthlyProcessDateResVo> planMonthlyProcessDateResVos, PlanMonthlyProcessResVo cutProcessResVo, Order order) {

        if (CollUtil.isEmpty(planMonthlyProcessDateResVos)) {
            cutProcessResVo.setTotalCount(BigDecimal.ZERO);
            cutProcessResVo.setFinishCount(BigDecimal.ZERO);
            return new ArrayList<>();
        }

        // 总数量
        PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo = planMonthlyProcessDateResVos.stream().min(Comparator.comparing(PlanMonthlyProcessDateResVo::getParentPartCount)).get();
        BigDecimal root = planMonthlyProcessDateResVo.getParentPartCount().multiply(new BigDecimal(order.getOrderQty()));
        cutProcessResVo.setTotalCount(root);

        // 比例
        Map<String, BigDecimal> biliMap = planMonthlyProcessDateResVos.stream().collect(Collectors.toMap(PlanMonthlyProcessDateResVo::getPlmId, PlanMonthlyProcessDateResVo::getPartCount, (e1, e2) -> e1));

        // 完成数量
        Map<String, BigDecimal> finishCount = planMonthlyProcessDateResVos.stream().filter(e -> LocalDate.parse(e.getDateStr(), DatePattern.NORM_DATE_FORMATTER).isBefore(startDate)).collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getPlmId, Collectors.reducing(BigDecimal.ZERO, PlanMonthlyProcessDateResVo::getFinishCount, BigDecimal::add)));
        BigDecimal bigDecimal1 = biliMap.entrySet()
                .stream()
                .map(e -> {
                    BigDecimal bigDecimal = finishCount.get(e.getKey());
                    if (bigDecimal == null) {
                        return new BigDecimal(0);
                    }
                    if (bigDecimal.intValue() == 0) {
                        return new BigDecimal(0);
                    }
                    return bigDecimal.divide(e.getValue(), 2, RoundingMode.DOWN);
                })
                .min(BigDecimal::compareTo)
                .orElseGet(() -> new BigDecimal(0));

        cutProcessResVo.setFinishCount(bigDecimal1);

        // 之后每一天的
        List<PlanMonthlyProcessCalendarResVo> collect = allDate.stream()
                .map(e -> {
                    PlanMonthlyProcessCalendarResVo calendarResVo = new PlanMonthlyProcessCalendarResVo();
                    calendarResVo.setDate(e);
                    return calendarResVo;
                })
                .sorted(Comparator.comparing(PlanMonthlyProcessCalendarResVo::getDate))
                .collect(Collectors.toList());


        Map<LocalDate, Integer> map = new HashMap<>();
        int sum = 0;
        for (PlanMonthlyProcessCalendarResVo currentDay : collect) {
            Map<String, BigDecimal> dayPlmIdCount = planMonthlyProcessDateResVos.stream().filter(e1 -> LocalDate.parse(e1.getDateStr(), DatePattern.NORM_DATE_FORMATTER).equals(currentDay.getDate())).collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getPlmId, Collectors.reducing(BigDecimal.ZERO, PlanMonthlyProcessDateResVo::getTotalCount, BigDecimal::add)));

            BigDecimal jiashu = biliMap.entrySet()
                    .stream()
                    .map(e -> {
                        BigDecimal bigDecimal = finishCount.get(e.getKey());
                        if (bigDecimal == null) {
                            bigDecimal = new BigDecimal(0);
                        }

                        BigDecimal dayCount = dayPlmIdCount.getOrDefault(e.getKey(), BigDecimal.ZERO);
                        BigDecimal newFinish = bigDecimal.add(dayCount);
                        finishCount.put(e.getKey(), newFinish);
                        return newFinish.divide(e.getValue(), 0, RoundingMode.DOWN);
                    })
                    .min(BigDecimal::compareTo)
                    .orElseGet(() -> new BigDecimal(0));
            map.put(currentDay.getDate(), jiashu.intValue());

            Integer orDefault = map.getOrDefault(currentDay.getDate().minusDays(1), bigDecimal1.intValue());

            currentDay.setQuantity(jiashu.intValue() - orDefault);
            sum += currentDay.getQuantity();
            currentDay.setFinishCount(sum);

        }

        return collect;
    }

    public List<PlanMbdOrderResVo> mbdOrder(PlanMbdOrderReqVo reqVo) {
        List<Order> orderList = orderMapper.lambdaQuery()
                .eq(Order::getOrderType, 2)
                .ge(Order::getOrderStatus, 6)
                .like(StringUtils.hasText(reqVo.getOrderCode()), Order::getOrderCode, reqVo.getOrderCode())
                .orderByDesc(Order::getId)
                .list();

        return orderList.stream()
                .map(e -> {
                    PlanMbdOrderResVo r = new PlanMbdOrderResVo();
                    BeanUtils.copyProperties(e, r);
                    if (StringUtils.hasText(r.getOrderCode())) {
                        r.setMaterCode(r.getOrderCode());
//                        r.setMaterCode(r.getMaterCode());
                    }
                    return r;
                })
                .collect(Collectors.toList());
    }


    private Map<String, Object> createPartTypeMap(String name, int value) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        map.put("value", value);
        return map;
    }

    @Transactional(readOnly = true)
    public BaseListResVo<PlanProResVo> pro(PlanProReqVo reqVo) {

        if (reqVo.getStartDate() == null) {
            reqVo.setStartDate(LocalDate.now());
        }

        if (reqVo.getEndDate() == null) {
            reqVo.setEndDate(reqVo.getStartDate().plusDays(7));
        }

        LocalDateTime startDateTime = LocalDateTime.of(reqVo.getStartDate(), LocalTime.of(6, 0, 0));
        LocalDateTime endDateTime = LocalDateTime.of(reqVo.getEndDate().plusDays(1), LocalTime.of(6, 0, 0));

        Map<Integer, List<Integer>> map = new HashMap<>();
        map.put(1, Arrays.asList(6, 7, 8));
        map.put(2, Arrays.asList(9, 10));

        List<Order> orderList = orderMapper.lambdaQuery()
                .eq(Order::getOrderType, 1)
                .like(StringUtils.hasText(reqVo.getConstructCode()), Order::getConstructNo, reqVo.getConstructCode())
                .in(Order::getOrderStatus, map.getOrDefault(reqVo.getOrderStatus(), Arrays.asList(6, 7, 8, 9, 10)))
                .in(CollUtil.isNotEmpty(reqVo.getSuffix()), Order::getSuffix, reqVo.getSuffix())
                .le(Order::getPlanStartDateTime, endDateTime.format(DatePattern.NORM_DATETIME_FORMATTER))
                .ge(Order::getPlanEndDateTime, startDateTime.format(DatePattern.NORM_DATETIME_FORMATTER))
                .isNotNull(Order::getSuffix)
                .list();

        if (CollUtil.isEmpty(orderList)) {
            return new BaseListResVo<>(Collections.emptyList());
        }

        long until = reqVo.getStartDate().until(reqVo.getEndDate(), ChronoUnit.DAYS);
        List<LocalDate> allDate = Stream.iterate(reqVo.getStartDate(), s -> s.plusDays(1)).limit(until + 1).collect(Collectors.toList());

        Map<String, Integer> orderSuffixMap = new HashMap<>();
        orderSuffixMap.put("D", 1);
        orderSuffixMap.put("Y", 2);
        orderSuffixMap.put("Z", 3);
        orderSuffixMap.put("C", 4);
        orderSuffixMap.put("L", 5);
        orderSuffixMap.put("T", 6);
        orderSuffixMap.put("B", 7);

        orderList.sort(Comparator.comparing(Order::getConstructNo).thenComparing(e -> orderSuffixMap.get(e.getSuffix())));

        List<Integer> orderIdList = orderList.stream().map(BaseEntity::getId).collect(Collectors.toList());

        Map<String, Phase> phaseMap = phaseMapper.lambdaQuery().isNotNull(Phase::getMyCode).list().stream().collect(Collectors.toMap(Phase::getMyCode, Function.identity()));

        List<OrderBom> orderBomList = orderBomMapper.lambdaQuery().in(OrderBom::getOrderId, orderIdList).list();
        Map<Integer, Map<Integer, OrderBom>> orderIdBomIdMap = orderBomList.stream().collect(Collectors.groupingBy(OrderBom::getOrderId, Collectors.toMap(OrderBom::getId, Function.identity())));

        // 焊接与整加区
        PlanProResVo weldingArea = weldingArea(orderList, orderIdList, allDate, phaseMap, orderIdBomIdMap, orderSuffixMap, orderBomList);

        // 配套区
        PlanProResVo partsAssemblyArea = partsAssemblyArea(orderList, orderIdList, allDate, phaseMap, orderIdBomIdMap);

        // 下料区
        PlanProResVo cuttingArea = cuttingArea(orderList, orderIdList, allDate, phaseMap, orderIdBomIdMap);

        List<PlanProResVo> list = Arrays.asList(weldingArea, partsAssemblyArea, cuttingArea);

        LocalDate startDate = LocalDate.now();

        // 计算重量
        for (PlanProResVo res : list) {
            for (PlanProAreaResVo area : res.getArea()) {
                area.setWorkDetail(area.getWorkDetail().stream().filter(e -> e.getPlanCount() > 0).collect(Collectors.toList()));

                List<PlanProDetailResVo> workDetail = area.getWorkDetail();
                for (PlanProDetailResVo planProDetailResVo : workDetail) {

                    for (PlanProDetailDayResVo day : planProDetailResVo.getDayList()) {
                        if (day.getPlanCount() == null || day.getPlanCount().intValue() == 0 || day.getFinishCount() == null || day.getFinishCount().intValue() == 0) {
                            day.setPlanRate(new BigDecimal(0));
                        } else {
                            day.setPlanRate(day.getFinishCount().divide(day.getPlanCount(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                        }
                    }

                    Optional<PlanProDetailDayResVo> max = planProDetailResVo.getDayList().stream().filter(e -> e.getDate().isBefore(startDate)).max(Comparator.comparing(PlanProDetailDayResVo::getDate));
                    if (max.isPresent()) {
                        PlanProDetailDayResVo planProDetailDayResVo = max.get();
                        if (planProDetailDayResVo.getTotalPlanCount() == null) {
                            planProDetailDayResVo.setTotalPlanCount(new BigDecimal(0));
                        }
                        if (planProDetailDayResVo.getTotalFinishCount() == null) {
                            planProDetailDayResVo.setTotalFinishCount(new BigDecimal(0));
                        }
                        planProDetailResVo.setDelayCount(planProDetailDayResVo.getTotalPlanCount().subtract(planProDetailDayResVo.getTotalFinishCount()).intValue());
                        if (planProDetailResVo.getDelayCount() < 0) {
                            planProDetailResVo.setDelayCount(0);
                        }
                    } else {
                        planProDetailResVo.setDelayCount(0);
                    }

                    if (planProDetailResVo.getPlanCount() == null || planProDetailResVo.getPlanCount() == 0 || planProDetailResVo.getFinishCount() == null || planProDetailResVo.getFinishCount() == 0) {
                        planProDetailResVo.setPlanRate(new BigDecimal(0));
                    } else {
                        BigDecimal reduce = planProDetailResVo.getDayList().stream().filter(e -> e.getDate().compareTo(reqVo.getStartDate()) >= 0 && e.getDate().compareTo(reqVo.getEndDate()) <= 0).map(e -> e.getFinishCount() == null ? new BigDecimal(0) : e.getFinishCount()).reduce(BigDecimal.ZERO, BigDecimal::add);
                        planProDetailResVo.setPlanRate(reduce.divide(new BigDecimal(planProDetailResVo.getPlanCount()), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                    }

                    planProDetailResVo.setTodoCount(planProDetailResVo.getTotalCount() - planProDetailResVo.getFinishCount());

                }

            }
        }

        List<Integer> orderIds = list.stream().flatMap(e -> e.getArea().stream()).map(PlanProAreaResVo::getOrderId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(orderIds)) {
            List<OrderPhase> orderPhaseList = orderPhaseMapper.lambdaQuery().in(OrderPhase::getOrderId, orderIds).list();
            Map<Integer, List<OrderPhase>> orderIdPhaseMap = orderPhaseList.stream().collect(Collectors.groupingBy(OrderPhase::getOrderId));

            for (PlanProResVo res : list) {

                for (PlanProAreaResVo area : res.getArea()) {
                    List<OrderPhase> orderPhaseList1 = orderIdPhaseMap.get(area.getOrderId());
                    if (CollUtil.isEmpty(orderPhaseList1)) {
                        continue;
                    }

                    Integer reduce = orderPhaseList1.stream().map(OrderPhase::getOriWorkTime).filter(Objects::nonNull).reduce(0, Integer::sum);

                    area.getWorkDetail().stream().map(PlanProDetailResVo::getPlanCount).max(Integer::compareTo).ifPresent(e -> {
                        area.setTotalHours(new BigDecimal(reduce * e).divide(new BigDecimal(3600), 0, RoundingMode.HALF_UP));
                    });

                }

                break;
            }

        }


        return new BaseListResVo<>(list);
    }

    public PlanProResVo cuttingArea(List<Order> orderList, List<Integer> allOrderId, List<LocalDate> allDay, Map<String, Phase> endDateTime, Map<Integer, Map<Integer, OrderBom>> orderIdBomIdMap) {
        PlanProResVo resVo = new PlanProResVo();
        resVo.setName("下料区");
        LocalDate yesterday = LocalDate.now().minusDays(1);

        List<PlanProAreaResVo> area = orderList.stream()
                .map(e -> {
                    PlanProAreaResVo areaResVo = new PlanProAreaResVo();
                    areaResVo.setOrderId(e.getId());
                    areaResVo.setConstructCode(e.getConstructNo());
                    areaResVo.setProjectName(e.getMaterName().replaceAll("[一二三四五六]拼", ""));
                    areaResVo.setTotalCount(e.getOrderQty());
                    areaResVo.setSuffix(e.getSuffix());
                    return areaResVo;
                })
                .collect(Collectors.toList());

        resVo.getArea().addAll(area);

        QueryWrapper<ApsWorkDetail> weldingWrapper = Wrappers.query();
        weldingWrapper.in("  awd.phase_code ", "JG", "HG");
        weldingWrapper.eq("  awd.type ", 2);
        weldingWrapper.in(" awd.order_id ", allOrderId);
        weldingWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");
        List<PlanProDetailDayResVo> dayDetail = apsWorkDetailMapper.cuttingArea(weldingWrapper);

        QueryWrapper<ApsWorkDetail> assemblyWrapper1 = Wrappers.query();
        assemblyWrapper1.in("  awd.phase_code ", "JG", "HG");
        assemblyWrapper1.eq("  awd.type ", 2);
        assemblyWrapper1.in(" awd.order_id ", allOrderId);
        assemblyWrapper1.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(DATE_SUB(wdt.create_date_time, INTERVAL 7 HOUR), '%Y-%m-%d')  ");
        List<PlanProDetailDayResVo> dayDetailFinish = apsWorkDetailMapper.cuttingAreaFinish(assemblyWrapper1, allOrderId.stream().map(Object::toString).collect(Collectors.joining(",")));

        if (!dayDetailFinish.isEmpty()) {
            dayDetail.addAll(dayDetailFinish);
        }

        for (PlanProDetailDayResVo planProDetailDayResVo : dayDetail) {
            if (planProDetailDayResVo.getDateStr() != null) {
                planProDetailDayResVo.setDate(LocalDate.parse(planProDetailDayResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
            } else {
                planProDetailDayResVo.setDate(LocalDate.parse(planProDetailDayResVo.getFinishDateStr(), DatePattern.NORM_DATE_FORMATTER));
            }
        }

        Map<Integer, List<PlanProDetailDayResVo>> orderIdBomMap = dayDetail.stream().collect(Collectors.groupingBy(PlanProDetailDayResVo::getOrderId));
//        Map<Integer, Map<Integer, Map<String, PlanProDetailDayResVo>>> orderIdBomIdDayMap = dayDetail.stream().collect(Collectors.groupingBy(PlanProDetailDayResVo::getOrderId, Collectors.groupingBy(PlanProDetailDayResVo::getBomId, Collectors.toMap(PlanProDetailDayResVo::getDateStr, Function.identity()))));

        String[] typeArr = {"P1", "P2", "P3" /*, "P4", "P5" */};
        Map<String, String> map = new HashMap<>();
        map.put("P1", "下料");
        map.put("P2", "下料");
        map.put("P3", "下料");
//        map.put("P4", "下料");
//        map.put("P5", "下料");


        for (PlanProAreaResVo areaR : area) {

            List<PlanProDetailDayResVo> integerMapMap = orderIdBomMap.get(areaR.getOrderId());

            if (CollUtil.isEmpty(integerMapMap)) {
                continue;
            }

            Map<Integer, OrderBom> integerOrderBomMap = orderIdBomIdMap.get(areaR.getOrderId());
            List<OrderBom> platePartList = integerOrderBomMap.values().stream().filter(e -> e.getWorkChain().contains("JG") || e.getWorkChain().contains("HG")).collect(Collectors.toList());
            Map<String, List<OrderBom>> assemblyMap = new HashMap<>();
            for (OrderBom orderBom : platePartList) {
                OrderBom parent = integerOrderBomMap.get(orderBom.getPid());
                findParent:
                while (parent != null) {
                    for (String type : typeArr) {
                        if (parent.getPlmId().contains(type)) {
                            assemblyMap.computeIfAbsent(type, k -> new ArrayList<>()).add(orderBom);
                            break findParent;
                        }
                    }
                    parent = integerOrderBomMap.get(parent.getPid());
                }
            }

            for (String s : assemblyMap.keySet()) {
                PlanProDetailResVo r = new PlanProDetailResVo();
                r.setPhaseName(map.get(s));
                r.setPhaseCode(s);
                r.setTotalCount(areaR.getTotalCount());
                r.setFinishCount(0);
                r.setTodoCount(0);
                r.setPlanCount(0);
                r.setPlanRate(new BigDecimal(0));
                areaR.getWorkDetail().add(r);

                List<PlanProDetailDayResVo> beforList = integerMapMap.stream().filter(e -> allDay.get(0).isAfter(e.getDate())).collect(Collectors.toList());

                // 计算成套数量

                int prePlan = calcPlanJiashu(beforList, assemblyMap.get(s));
                int proPlan = calcProJiashu(beforList, assemblyMap.get(s));
                for (LocalDate day : allDay) {
                    PlanProDetailDayResVo dayData = new PlanProDetailDayResVo();

                    dayData.setDate(day);
                    List<PlanProDetailDayResVo> collect = integerMapMap.stream().filter(e -> e.getDate().equals(day)).collect(Collectors.toList());
                    if (CollUtil.isNotEmpty(collect)) {
                        beforList.addAll(collect);
                        int i = calcPlanJiashu(beforList, assemblyMap.get(s));
                        dayData.setPlanCount(new BigDecimal(i - prePlan));
                        dayData.setTotalPlanCount(new BigDecimal(i));
                        prePlan = i;

                        int i1 = calcProJiashu(beforList, assemblyMap.get(s));
                        dayData.setFinishCount(new BigDecimal(i1 - proPlan));
                        dayData.setTotalFinishCount(new BigDecimal(i1));
                        proPlan = i1;

                    }
                    if (dayData.getPlanCount() != null && dayData.getPlanCount().intValue() > 0) {
                        dayData.setPlanRate(dayData.getFinishCount().divide(dayData.getPlanCount(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                    }
                    r.getDayList().add(dayData);
                }

                r.setPlanCount(r.getDayList().stream().reduce(0, (a, b) -> a + (b.getPlanCount() == null ? 0 : b.getPlanCount().intValue()), Integer::sum));
                r.setDaysFinishCount(r.getDayList().stream().reduce(0, (a, b) -> a + (b.getFinishCount() == null ? 0 : b.getFinishCount().intValue()), Integer::sum));
                r.setFinishCount(r.getDayList().stream().map(PlanProDetailDayResVo::getTotalFinishCount).filter(Objects::nonNull).max(BigDecimal::compareTo).orElseGet(() -> BigDecimal.ZERO).intValue());

                Optional<PlanProDetailDayResVo> first = r.getDayList().stream().filter(e -> yesterday.equals(e.getDate())).findFirst();
//                if(first.isPresent() && first.get().getTotalPlanCount() != null && first.get().getTotalPlanCount().compareTo(BigDecimal.ZERO) > 0){
//                    r.setDelayCount(first.get().getTotalFinishCount().divide(first.get().getTotalPlanCount(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).intValue());
//                }

                if (r.getPlanCount() != 0) {
                    r.setPlanRate(new BigDecimal(r.getDaysFinishCount()).divide(new BigDecimal(r.getPlanCount()), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }
                r.setTodoCount(r.getPlanCount() - r.getFinishCount());
            }


        }

        return resVo;
    }

    private int calcProJiashu(List<PlanProDetailDayResVo> beforList, List<OrderBom> orderBoms) {
        Map<Integer, List<PlanProDetailDayResVo>> bomIdMap = beforList.stream().collect(Collectors.groupingBy(PlanProDetailDayResVo::getBomId));

        Optional<BigDecimal> min = orderBoms.stream()
                .map(e -> {
                    List<PlanProDetailDayResVo> planProDetailDayResVos = bomIdMap.get(e.getId());
                    if (CollUtil.isEmpty(planProDetailDayResVos)) {
                        return new BigDecimal(0);
                    }
                    return planProDetailDayResVos.stream().filter(e1 -> e1.getFinishCount() != null).map(PlanProDetailDayResVo::getFinishCount).reduce(BigDecimal.ZERO, BigDecimal::add).divide(e.getTotalCount(), 0, RoundingMode.DOWN);
                })
                .min(BigDecimal::compareTo);
        return min.map(BigDecimal::intValue).orElse(0);

    }

    public int calcPlanJiashu(List<PlanProDetailDayResVo> beforList, List<OrderBom> orderBoms) {
        Map<Integer, List<PlanProDetailDayResVo>> bomIdMap = beforList.stream().collect(Collectors.groupingBy(PlanProDetailDayResVo::getBomId));

        Optional<BigDecimal> min = orderBoms.stream()
                .map(e -> {
                    List<PlanProDetailDayResVo> planProDetailDayResVos = bomIdMap.get(e.getId());
                    if (CollUtil.isEmpty(planProDetailDayResVos)) {
                        return new BigDecimal(0);
                    }
                    return planProDetailDayResVos.stream().filter(e1 -> e1.getPlanCount() != null).map(PlanProDetailDayResVo::getPlanCount).reduce(BigDecimal.ZERO, BigDecimal::add).divide(e.getTotalCount(), 0, RoundingMode.DOWN);
                })
                .min(BigDecimal::compareTo);
        return min.map(BigDecimal::intValue).orElse(0);

    }

    public PlanProResVo partsAssemblyArea(List<Order> orderList, List<Integer> allOrderId, List<LocalDate> allDay, Map<String, Phase> endDateTime, Map<Integer, Map<Integer, OrderBom>> orderIdBomIdMap) {
        PlanProResVo resVo = new PlanProResVo();
        resVo.setName("配套区");

        LocalDate yesterday = LocalDate.now().minusDays(1);

        List<PlanProAreaResVo> area = orderList.stream()
                .map(e -> {
                    PlanProAreaResVo areaResVo = new PlanProAreaResVo();
                    areaResVo.setOrderId(e.getId());
                    areaResVo.setConstructCode(e.getConstructNo());
                    areaResVo.setProjectName(e.getMaterName().replaceAll("[一二三四五六]拼", ""));
                    areaResVo.setTotalCount(e.getOrderQty());
                    areaResVo.setSuffix(e.getSuffix());
                    return areaResVo;
                })
                .collect(Collectors.toList());

        resVo.getArea().addAll(area);

        QueryWrapper<ApsWorkOrder> assemblyWrapper = Wrappers.query();
        assemblyWrapper.ne("  work_chain ", "WL");
        assemblyWrapper.in(" awo.order_id ", allOrderId);
        assemblyWrapper.groupBy("  awo.order_id, awo.bom_id, awo.bom_pid, awo.plm_id, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");
        List<PlanProDetailDayResVo> dayDetail = apsWorkOrderMapper.partsAssemblyArea(assemblyWrapper, allOrderId.stream().map(Object::toString).collect(Collectors.joining(",")));

        QueryWrapper<ApsWorkOrder> assemblyWrapper1 = Wrappers.query();
        assemblyWrapper1.ne("  work_chain ", "WL");
        assemblyWrapper1.in(" awo.order_id ", allOrderId);
        assemblyWrapper1.groupBy("  awo.order_id, awo.bom_id, awo.bom_pid, awo.plm_id, DATE_FORMAT(DATE_SUB(wdt.create_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");
        List<PlanProDetailDayResVo> dayDetailFinish = apsWorkOrderMapper.partsAssemblyAreaFinish(assemblyWrapper1, allOrderId.stream().map(Object::toString).collect(Collectors.joining(",")));

        if (!dayDetailFinish.isEmpty()) {
            dayDetail.addAll(dayDetailFinish);
        }
//        List<PlanProDetailDayResVo> dayDetail =  new ArrayList<>();

        for (PlanProDetailDayResVo planProDetailDayResVo : dayDetail) {
            if (planProDetailDayResVo.getDateStr() != null) {
                planProDetailDayResVo.setDate(LocalDate.parse(planProDetailDayResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
            } else {
                planProDetailDayResVo.setDate(LocalDate.parse(planProDetailDayResVo.getFinishDateStr(), DatePattern.NORM_DATE_FORMATTER));
            }
        }

        Map<Integer, List<PlanProDetailDayResVo>> orderIdBomMap = dayDetail.stream().collect(Collectors.groupingBy(PlanProDetailDayResVo::getOrderId));

        String[] typeArr = {"P1", "P2", "P3" /*, "P4", "P5" */};
        Map<String, String> map = new HashMap<>();
        map.put("P1", "配套");
        map.put("P2", "配套");
        map.put("P3", "配套");
//        map.put("P4", "配套");
//        map.put("P5", "配套");

        for (PlanProAreaResVo areaR : area) {

            List<PlanProDetailDayResVo> integerMapMap = orderIdBomMap.get(areaR.getOrderId());

            if (CollUtil.isEmpty(integerMapMap)) {
                continue;
            }

            Map<Integer, OrderBom> integerOrderBomMap = orderIdBomIdMap.get(areaR.getOrderId());
            Map<Integer, List<OrderBom>> collect1 = integerOrderBomMap.values().stream().filter(e -> e.getPid() != null).collect(Collectors.groupingBy(OrderBom::getPid));
//            List<OrderBom> platePartList = integerOrderBomMap.values().stream().filter(e -> e.getWorkChain().contains("JG") || e.getWorkChain().contains("HG")).collect(Collectors.toList());
            Map<String, List<OrderBom>> assemblyMap = new HashMap<>();

            for (Map.Entry<Integer, List<OrderBom>> entry : collect1.entrySet()) {
                OrderBom p = integerOrderBomMap.get(entry.getKey());
                for (String type : typeArr) {
                    if (p.getPlmId().contains(type)) {
                        assemblyMap.put(type, entry.getValue());
                        entry.getValue().removeIf(e -> {
                            if (e.getWorkChain().equals("WL")) {
                                return true;
                            }

                            for (String s : typeArr) {
                                if (e.getPlmId().contains(s)) {
                                    return true;
                                }
                            }
                            return false;
                        });
                        break;
                    }
                }

            }
//            for (OrderBom orderBom : platePartList) {
//                OrderBom parent = integerOrderBomMap.get(orderBom.getPid());
//                findParent: while(parent != null){
//                    for (String type : typeArr) {
//                        if (parent.getPlmId().contains(type)) {
//                            assemblyMap.computeIfAbsent(type, k -> new ArrayList<>()).add(orderBom);
//                            break findParent;
//                        }
//                    }
//                    parent = integerOrderBomMap.get(parent.getPid());
//                }
//            }

            for (String s : assemblyMap.keySet()) {
                PlanProDetailResVo r = new PlanProDetailResVo();
                r.setPhaseName(map.get(s));
                r.setPhaseCode(s);
                r.setTotalCount(areaR.getTotalCount());
                r.setFinishCount(0);
                r.setTodoCount(0);
                r.setPlanCount(0);
                r.setPlanRate(new BigDecimal(0));
                areaR.getWorkDetail().add(r);

                List<PlanProDetailDayResVo> beforList = integerMapMap.stream().filter(e -> e.getDate() != null && allDay.get(0).isAfter(e.getDate())).collect(Collectors.toList());

                // 计算成套数量

                int prePlan = calcPlanJiashu(beforList, assemblyMap.get(s));
                int proPlan = calcProJiashu(beforList, assemblyMap.get(s));
                for (LocalDate day : allDay) {
                    PlanProDetailDayResVo dayData = new PlanProDetailDayResVo();

                    dayData.setDate(day);
                    List<PlanProDetailDayResVo> collect = integerMapMap.stream().filter(e -> e.getDate() != null && e.getDate().equals(day)).collect(Collectors.toList());
                    if (CollUtil.isNotEmpty(collect)) {
                        beforList.addAll(collect);
                        int i = calcPlanJiashu(beforList, assemblyMap.get(s));
                        dayData.setPlanCount(new BigDecimal(i - prePlan));
                        prePlan = i;

                        int i1 = calcProJiashu(beforList, assemblyMap.get(s));
                        dayData.setFinishCount(new BigDecimal(i1 - proPlan));
                        proPlan = i1;

                        dayData.setTotalPlanCount(new BigDecimal(prePlan));
                        dayData.setTotalFinishCount(new BigDecimal(proPlan));
                    }

                    if (dayData.getPlanCount() != null && dayData.getPlanCount().intValue() > 0) {
                        dayData.setPlanRate(dayData.getFinishCount().divide(dayData.getPlanCount(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                    }
                    r.getDayList().add(dayData);

                }

                r.setPlanCount(r.getDayList().stream().reduce(0, (a, b) -> a + (b.getPlanCount() == null ? 0 : b.getPlanCount().intValue()), Integer::sum));
//                r.setFinishCount(r.getDayList().stream().reduce(0, (a, b) -> a + (b.getFinishCount() == null?0:b.getFinishCount().intValue()), Integer::sum));
                r.setDaysFinishCount(r.getDayList().stream().reduce(0, (a, b) -> a + (b.getFinishCount() == null ? 0 : b.getFinishCount().intValue()), Integer::sum));
                r.setFinishCount(r.getDayList().stream().map(PlanProDetailDayResVo::getTotalFinishCount).filter(Objects::nonNull).max(BigDecimal::compareTo).orElseGet(() -> BigDecimal.ZERO).intValue());

                Optional<PlanProDetailDayResVo> first = r.getDayList().stream().filter(e -> yesterday.equals(e.getDate())).findFirst();
//                if(first.isPresent() && first.get().getTotalPlanCount() != null && first.get().getTotalPlanCount().compareTo(BigDecimal.ZERO) > 0){
//                    r.setDelayCount(first.get().getTotalFinishCount().divide(first.get().getTotalPlanCount(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).intValue());
//                }


                if (r.getPlanCount() != 0) {
                    r.setPlanRate(new BigDecimal(r.getDaysFinishCount()).divide(new BigDecimal(r.getPlanCount()), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }
                r.setTodoCount(r.getPlanCount() - r.getFinishCount());
            }


        }

        return resVo;
    }

    public PlanProResVo weldingArea(List<Order> orderList, List<Integer> allOrderId, List<LocalDate> allDay, Map<String, Phase> phaseMap, Map<Integer, Map<Integer, OrderBom>> orderIdBomIdMap, Map<String, Integer> orderSuffixMap, List<OrderBom> orderBomList) {
        PlanProResVo resVo = new PlanProResVo();
        resVo.setName("焊接与整加区");
        LocalDate yesterday = LocalDate.now().minusDays(1);

        List<PlanProAreaResVo> area = orderList.stream()
                .map(e -> {
                    PlanProAreaResVo areaResVo = new PlanProAreaResVo();
                    areaResVo.setOrderId(e.getId());
                    areaResVo.setConstructCode(e.getConstructNo());
                    areaResVo.setProjectName(e.getMaterName().replaceAll("[一二三四五六]拼", ""));
                    areaResVo.setTotalCount(e.getOrderQty());
                    areaResVo.setSuffix(e.getSuffix());
                    return areaResVo;
                })
                .collect(Collectors.toList());

        resVo.getArea().addAll(area);
        Map<String, Integer> params = new HashMap<>();
        params.put("PZ", 1);
        params.put("WH", 2);
        params.put("ZT", 3);
        params.put("PT", 4);
        params.put("QM", 5);

        QueryWrapper<ApsWorkDetail> weldingWrapper1 = Wrappers.query();
        weldingWrapper1.in("  awd.phase_code ", "PZ");
        weldingWrapper1.eq("  awd.type ", 2);
        weldingWrapper1.eq(" awd.deleted ", 0);
        weldingWrapper1.eq(" awo.deleted ", 0);
        weldingWrapper1.like("  awd.plm_id ", "P1");
        weldingWrapper1.in(" awd.order_id ", allOrderId);
        weldingWrapper1.groupBy("  awd.order_id,  awd.phase_code, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d'), DATE_FORMAT(DATE_SUB(awd.actual_end_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");
        List<PlanProDetailDayResVo> dayDetail1 = apsWorkDetailMapper.weldingArea1(weldingWrapper1);



        QueryWrapper<ApsWorkDetail> weldingWrapper = Wrappers.query();
        weldingWrapper.in("  awd.phase_code ", "WH", "ZT", "PT");
        weldingWrapper.eq("  awd.type ", 2);
        weldingWrapper.eq(" awd.deleted ", 0);
        weldingWrapper.eq(" awo.deleted ", 0);
        weldingWrapper.in(" awd.order_id ", allOrderId);
        weldingWrapper.groupBy("  awd.order_id, awd.phase_code, awo.bom_id, awo.bom_pid, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d'), DATE_FORMAT(DATE_SUB(awd.actual_end_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");
        List<PlanProDetailDayResVo> dayDetail = apsWorkDetailMapper.weldingArea(weldingWrapper);



        QueryWrapper<ApsWorkDetail> weldingWrapper12 = Wrappers.query();
        weldingWrapper12.in("  awd.phase_code ", "PZ");
        weldingWrapper12.eq("  awd.type ", 2);
        weldingWrapper12.eq("  awd.deleted ", 0);
        weldingWrapper12.like(" awd.plm_id ", "P1");
        weldingWrapper12.in(" awd.order_id ", allOrderId);
        weldingWrapper12.groupBy("  awd.order_id,  awd.phase_code, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d')");
        List<PlanProDetailDayResVo> dayDetail12 = apsWorkDetailMapper.weldingArea12(weldingWrapper12);
        List<Integer> collect = dayDetail12.stream().map(PlanProDetailDayResVo::getOrderId).distinct().collect(Collectors.toList());
        if (CollUtil.isNotEmpty(collect)) {
            for (PlanProDetailDayResVo planProDetailDayResVo : dayDetail1) {
                if (collect.contains(planProDetailDayResVo.getOrderId())) {
                    planProDetailDayResVo.setPlanCount(BigDecimal.ZERO);
                }
            }
        }
        QueryWrapper<ApsWorkDetail> weldingWrapper22 = Wrappers.query();
        weldingWrapper22.in("  awd.phase_code ", "WH", "ZT", "PT");
        weldingWrapper22.eq("  awd.type ", 2);
        weldingWrapper22.eq("  awd.deleted ", 0);
        weldingWrapper22.in(" awd.order_id ", allOrderId);
        weldingWrapper22.groupBy("  awd.order_id, awd.phase_code, awd.bom_id, awd.bom_pid, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");
        List<PlanProDetailDayResVo> dayDetail22 = apsWorkDetailMapper.weldingArea22(weldingWrapper22);
        List<Integer> collect1 = dayDetail22.stream().map(PlanProDetailDayResVo::getOrderId).distinct().collect(Collectors.toList());
        if (CollUtil.isNotEmpty(collect1)) {
            for (PlanProDetailDayResVo planProDetailDayResVo : dayDetail) {
                if (collect1.contains(planProDetailDayResVo.getOrderId())) {
                    planProDetailDayResVo.setPlanCount(BigDecimal.ZERO);
                }
            }
        }


        Map<Integer, Order> odMap = orderList.stream().collect(Collectors.toMap(BaseEntity::getId, e -> e));
        List<String> sandajian = Arrays.asList("D", "Y", "Z");
        List<OrderBom> notPtBom = orderBomList.stream().filter(e -> e.getLevel() == 1 && odMap.get(e.getOrderId()) != null && sandajian.contains(odMap.get(e.getOrderId()).getSuffix())).filter(e -> !e.getWorkChain().endsWith("PT")).collect(Collectors.toList());
        if(CollUtil.isNotEmpty(notPtBom)){
            for (OrderBom orderBom : notPtBom) {
                String[] split = orderBom.getWorkChain().split("-");
                QueryWrapper<ApsWorkDetail> weldingWrapper2 = Wrappers.query();
                weldingWrapper2.in(" awd.phase_code ", split[split.length - 1]);
                weldingWrapper2.eq(" awd.group_code", "4-2_QMGW_00");
                weldingWrapper2.eq(" awd.deleted ", 0);
                weldingWrapper2.eq(" awo.deleted ", 0);
                weldingWrapper2.eq(" awd.type ", 2);
                weldingWrapper2.in(" awo.bom_id ", orderBom.getId());
                weldingWrapper2.groupBy("  awd.order_id, awd.phase_code, awo.bom_id, awo.bom_pid, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d'), DATE_FORMAT(DATE_SUB(awd.actual_end_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");
                List<PlanProDetailDayResVo> dayDetail2 = apsWorkDetailMapper.weldingArea(weldingWrapper2);
                for (PlanProDetailDayResVo planProDetailDayResVo : dayDetail2) {
                    planProDetailDayResVo.setPlanCount(BigDecimal.ZERO);
                }

                dayDetail.addAll(dayDetail2);

                QueryWrapper<ApsWorkDetail> weldingWrapper33 = Wrappers.query();
                weldingWrapper33.in("  awd.phase_code ", split[split.length - 1]);
                weldingWrapper33.eq("awd.group_code", "4-2_QMGW_00");
                weldingWrapper33.eq("  awd.deleted ", 0);
                weldingWrapper33.eq("  awd.type ", 2);
                weldingWrapper33.in(" awd.bom_id ", orderBom.getId());
                List<PlanProDetailDayResVo> dayDetail33 = apsWorkDetailMapper.weldingArea33(weldingWrapper33);
                dayDetail.addAll(dayDetail33);
            }

        }


        String[] typeArr = {"P1", "P2", "P3" /*, "P4", "P5" */};
        Map<String, String> map = new HashMap<>();
        map.put("P1", "一拼");
        map.put("P2", "二拼");
        map.put("P3", "三拼");
        map.put("P4", "四拼");
        map.put("P5", "五拼");

        if (CollUtil.isNotEmpty(dayDetail1)) {
            dayDetail.addAll(dayDetail1);
        }

        if(CollUtil.isNotEmpty(dayDetail12)){
            dayDetail.addAll(dayDetail12);
        }
        if(CollUtil.isNotEmpty(dayDetail22)){
            dayDetail.addAll(dayDetail22);
        }


        for (PlanProDetailDayResVo planProDetailDayResVo : dayDetail) {
            planProDetailDayResVo.setDate(LocalDate.parse(planProDetailDayResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
            if (planProDetailDayResVo.getFinishDateStr() != null) {
                planProDetailDayResVo.setFinishDate(LocalDate.parse(planProDetailDayResVo.getFinishDateStr(), DatePattern.NORM_DATE_FORMATTER));
            }
        }


        Map<Integer, Map<String, List<PlanProDetailDayResVo>>> orderIdPhaseCodeMap = dayDetail.stream().collect(Collectors.groupingBy(PlanProDetailDayResVo::getOrderId, Collectors.groupingBy(PlanProDetailDayResVo::getPhaseCode)));

        for (PlanProAreaResVo areaR : area) {

            Map<String, List<PlanProDetailDayResVo>> stringListMap = orderIdPhaseCodeMap.get(areaR.getOrderId());

            if (CollUtil.isEmpty(stringListMap)) {
                continue;
            }
            String projectName = areaR.getProjectName();
            areaR.setProjectName(projectName.replaceAll("[一二三四五六]拼", ""));

            for (Map.Entry<String, List<PlanProDetailDayResVo>> entry : stringListMap.entrySet()) {

                String phaseCode = entry.getKey();
                List<PlanProDetailDayResVo> planProDetailDayResVos = entry.getValue();

                PlanProDetailResVo r = new PlanProDetailResVo();
                r.setPhaseCode(phaseCode);
                if (r.getPhaseCode().equals("WH")) {
                    r.setPhaseName("转加");
                } else if (r.getPhaseCode().equals("PZ")) {
                    r.setPhaseName("一拼");
                } else if (r.getPhaseCode().equals("ZT")) {
                    r.setPhaseName("整加");
                } else {
                    r.setPhaseName(phaseMap.get(phaseCode).getName());
                }
                r.setTotalCount(areaR.getTotalCount());
                r.setFinishCount(0);
                r.setTodoCount(0);
                r.setPlanCount(0);
                r.setPlanRate(new BigDecimal(0));
                areaR.getWorkDetail().add(r);

                if ((projectName.contains("侧板") || projectName.contains("连杆")) && (phaseCode.equals("WH") || phaseCode.equals("PT"))) {

                    List<PlanProDetailDayResVo> phaseList = planProDetailDayResVos.stream().filter(e -> e.getPhaseCode().equals(phaseCode)).collect(Collectors.toList());
                    int prePlan = calcPlanJiashu(phaseList.stream().filter(e -> e.getDate().isBefore(allDay.get(0))).collect(Collectors.toList()), orderIdBomIdMap.get(areaR.getOrderId()).values().stream().filter(e -> e.getLevel().equals(2)).collect(Collectors.toList()));
                    int proPlan = calcProJiashu(phaseList.stream().filter(e -> e.getFinishDate() != null && e.getFinishDate().isBefore(allDay.get(0))).collect(Collectors.toList()), orderIdBomIdMap.get(areaR.getOrderId()).values().stream().filter(e -> e.getLevel().equals(2)).collect(Collectors.toList()));
                    for (LocalDate day : allDay) {
                        PlanProDetailDayResVo dayData = new PlanProDetailDayResVo();
                        dayData.setDate(day);

                        int sumFinishCount = calcProJiashu(phaseList.stream().filter(e -> e.getFinishDate() != null && !e.getFinishDate().isAfter(day)).collect(Collectors.toList()), orderIdBomIdMap.get(areaR.getOrderId()).values().stream().filter(e -> e.getLevel().equals(2)).collect(Collectors.toList()));
                        int sumTotalCount = calcPlanJiashu(phaseList.stream().filter(e -> !e.getDate().isAfter(day)).collect(Collectors.toList()), orderIdBomIdMap.get(areaR.getOrderId()).values().stream().filter(e -> e.getLevel().equals(2)).collect(Collectors.toList()));

                        dayData.setFinishCount(new BigDecimal(sumFinishCount - proPlan));
                        dayData.setPlanCount(new BigDecimal(sumTotalCount - prePlan));
                        prePlan = sumTotalCount;
                        proPlan = sumFinishCount;
                        dayData.setTotalPlanCount(new BigDecimal(sumTotalCount));
                        dayData.setTotalFinishCount(new BigDecimal(sumFinishCount));

                        if (dayData.getPlanCount() != null && dayData.getPlanCount().intValue() > 0) {
                            dayData.setPlanRate(dayData.getFinishCount().divide(dayData.getPlanCount(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                        }
                        r.getDayList().add(dayData);
                    }

                } else {
                    BigDecimal sumFinishCount = null;
                    BigDecimal sumTotalCount = null;

                    List<PlanProDetailDayResVo> phaseList = planProDetailDayResVos.stream().filter(e -> e.getPhaseCode().equals(phaseCode)).collect(Collectors.toList());
                    int prePlan = phaseList.stream().filter(e -> e.getDate().isBefore(allDay.get(0))).map(PlanProDetailDayResVo::getPlanCount).reduce(BigDecimal.ZERO, BigDecimal::add).intValue();
                    int proPlan = phaseList.stream().filter(e -> e.getFinishDate() != null && e.getFinishDate().isBefore(allDay.get(0))).map(PlanProDetailDayResVo::getFinishCount).reduce(BigDecimal.ZERO, BigDecimal::add).intValue();
                    for (LocalDate day : allDay) {
                        PlanProDetailDayResVo dayData = new PlanProDetailDayResVo();
                        dayData.setDate(day);

                        sumTotalCount = phaseList.stream().filter(e -> !e.getDate().isAfter(day)).map(PlanProDetailDayResVo::getPlanCount).reduce(BigDecimal.ZERO, BigDecimal::add);
                        sumFinishCount = phaseList.stream().filter(e -> e.getFinishDate() != null && !e.getFinishDate().isAfter(day)).map(PlanProDetailDayResVo::getFinishCount).reduce(BigDecimal.ZERO, BigDecimal::add);

                        dayData.setPlanCount(sumTotalCount.subtract(new BigDecimal(prePlan)));
                        dayData.setFinishCount(sumFinishCount.subtract(new BigDecimal(proPlan)));
                        prePlan = sumTotalCount.intValue();
                        proPlan = sumFinishCount.intValue();
                        dayData.setTotalPlanCount(sumTotalCount);
                        dayData.setTotalFinishCount(sumFinishCount);
                        if (dayData.getPlanCount() != null && dayData.getPlanCount().intValue() > 0) {
                            dayData.setPlanRate(dayData.getFinishCount().divide(dayData.getPlanCount(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                        }

                        r.getDayList().add(dayData);
                    }
                }
                areaR.getWorkDetail().sort(Comparator.comparingInt(a -> params.get(a.getPhaseCode())));

                r.setPlanCount(r.getDayList().stream().reduce(0, (a, b) -> a + (b.getPlanCount() == null ? 0 : b.getPlanCount().intValue()), Integer::sum));
                r.setDaysFinishCount(r.getDayList().stream().reduce(0, (a, b) -> a + (b.getFinishCount() == null ? 0 : b.getFinishCount().intValue()), Integer::sum));
                r.setFinishCount(r.getDayList().stream().map(PlanProDetailDayResVo::getTotalFinishCount).filter(Objects::nonNull).max(BigDecimal::compareTo).orElseGet(() -> BigDecimal.ZERO).intValue());

//                Optional<PlanProDetailDayResVo> first = r.getDayList().stream().filter(e -> yesterday.equals(e.getDate())).findFirst();
//                if(first.isPresent() && first.get().getTotalPlanCount() != null && first.get().getTotalPlanCount().compareTo(BigDecimal.ZERO) > 0){
//                    r.setDelayCount(first.get().getTotalFinishCount().divide(first.get().getTotalPlanCount(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).intValue());
//                }

                r.setTodoCount(r.getPlanCount() - r.getFinishCount());
            }


            Integer i = orderSuffixMap.get(areaR.getSuffix());
            Optional<PlanProDetailResVo> wh = areaR.getWorkDetail().stream().filter(e -> e.getPhaseCode().equals("WH")).findFirst();
            if (wh.isPresent()) {
                BigDecimal reduce = orderIdBomIdMap.get(areaR.getOrderId()).values().stream().filter(e -> e.getLevel().equals(i <= 3 ? 1 : 2)).filter(e -> e.getMaterWeight() != null).map(OrderBom::getMaterWeight).reduce(BigDecimal.ZERO, BigDecimal::add).multiply(new BigDecimal(wh.get().getPlanCount())).divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP);
                areaR.setPlanWeight(reduce);
            }
        }


        return resVo;
    }
}